diff --git a/go.mod b/go.mod index a64125cf9a..e6cef3bbed 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/docker/docker v0.7.3-0.20190806133308-ecdb0b22393b github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/ethereum/go-ethereum v1.9.2 + github.com/ethereum/go-ethereum v1.9.7 github.com/ethersphere/go-sw3 v0.2.1 github.com/fatih/color v1.7.0 // indirect github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc @@ -45,7 +45,7 @@ require ( github.com/rnsdomains/rns-go-lib v0.0.0-20191114120302-3505575b0b8f github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d // indirect github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 - github.com/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965 + github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d github.com/tilinna/clock v1.0.2 github.com/uber-go/atomic v1.4.0 // indirect github.com/uber/jaeger-client-go v0.0.0-20180607151842-f7e0d4744fa6 diff --git a/go.sum b/go.sum index 3f65912b67..7a7534b071 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,9 @@ github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa h1:o8OuEkracbk3qH6GvlI6XpEN1HTSxkzOG42xZpfDv/s= github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/ethereum/go-ethereum v1.9.2 h1:RMIHDO/diqXEgORSVzYx8xW9x2+S32PoAX5lQwya0Lw= github.com/ethereum/go-ethereum v1.9.2/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= +github.com/ethereum/go-ethereum v1.9.7 h1:p4O+z0MGzB7xxngHbplcYNloxkFwGkeComhkzWnq0ig= +github.com/ethereum/go-ethereum v1.9.7/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= github.com/ethersphere/go-sw3 v0.2.1 h1:+i660uWzhRbT1YO7MAeuxzn+jUeYOTc8rGRVjsKaw+4= github.com/ethersphere/go-sw3 v0.2.1/go.mod h1:HukT0aZ6QdW/d7zuD/0g5xlw6ewu9QeqHojxLDsaERQ= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -77,11 +78,9 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gballet/go-libpcsclite v0.0.0-20190528105824-2fd9b619dd3c h1:gID5iWto0hEmbyMl+15Rkju0P+8uvF0jSn1cWdyv+5M= github.com/gballet/go-libpcsclite v0.0.0-20190528105824-2fd9b619dd3c/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -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-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.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= @@ -90,7 +89,6 @@ github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNI 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.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -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= @@ -98,7 +96,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 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= @@ -122,7 +119,6 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/graph-gophers/graphql-go v0.0.0-20190724201507-010347b5f9e6 h1:9WiNlI9Cds5S5YITwRpRs8edNaq0nxTEymhDW20A1QE= github.com/graph-gophers/graphql-go v0.0.0-20190724201507-010347b5f9e6/go.mod h1:Au3iQ8DvDis8hZ4q2OzRcaKYlAsPt+fYvib5q4nIqu4= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -145,10 +141,10 @@ github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8 h1:VhnqxaTIudc9IWKx8uXRLnpdSb9noCEj+vHacjmhp68= github.com/karalabe/usb v0.0.0-20190819132248-550797b1cad8/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -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/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= @@ -165,7 +161,6 @@ github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 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-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -211,7 +206,6 @@ github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7ir github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/peterh/liner v0.0.0-20190123174540-a2c9a5303de7 h1:Imx0QZXGB4siHjlmDJ/kx/bU+D36ytDj5dgy/TkIQ+A= github.com/peterh/liner v0.0.0-20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 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= @@ -234,15 +228,12 @@ github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d h1:ouzpe+YhpIfnj github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00 h1:8DPul/X0IT/1TNMIxoKLwdemEOBBHDC/K4EB16Cw5WE= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20= -github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d h1:8Tt7DYYdFqLlOIuyiE0RluKem4T+048AUafnIjH80wg= github.com/rs/xhandler v0.0.0-20170707052532-1eb70cf1520d/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -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/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -257,8 +248,9 @@ 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/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965 h1:V/AztY/q2oW5ghho7YMgUJQkKvSACHRxpeDyT5DxpIo= github.com/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tilinna/clock v1.0.2 h1:6BO2tyAC9JbPExKH/z9zl44FLu1lImh3nDNKA0kgrkI= github.com/tilinna/clock v1.0.2/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao= github.com/tyler-smith/go-bip39 v0.0.0-20181017060643-dbb3b84ba2ef h1:luEzjJzktS9eU0CmI0uApXHLP/lKzOoRPrJhd71J8ik= @@ -279,7 +271,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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= @@ -312,7 +303,6 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M= golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -326,7 +316,6 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b h1:mSUCVIwDx4hfXJfWsOPfdzEHxzb2Xjl6BQ8YgPnazQA= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 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= diff --git a/vendor/github.com/ethereum/go-ethereum/.travis.yml b/vendor/github.com/ethereum/go-ethereum/.travis.yml index 3a40ff5834..4acd00bc9f 100644 --- a/vendor/github.com/ethereum/go-ethereum/.travis.yml +++ b/vendor/github.com/ethereum/go-ethereum/.travis.yml @@ -7,7 +7,7 @@ jobs: - stage: lint os: linux dist: xenial - go: 1.12.x + go: 1.13.x env: - lint git: @@ -18,15 +18,15 @@ jobs: - stage: build os: linux dist: xenial - go: 1.10.x + go: 1.11.x script: - - go run build/ci.go install - - go run build/ci.go test -coverage $TEST_PACKAGES + - go run build/ci.go install + - go run build/ci.go test -coverage $TEST_PACKAGES - stage: build os: linux dist: xenial - go: 1.11.x + go: 1.12.x script: - go run build/ci.go install - go run build/ci.go test -coverage $TEST_PACKAGES @@ -35,14 +35,14 @@ jobs: - stage: build os: linux dist: xenial - go: 1.12.x + go: 1.13.x script: - go run build/ci.go install - go run build/ci.go test -coverage $TEST_PACKAGES - stage: build os: osx - go: 1.12.x + go: 1.13.x script: - echo "Increase the maximum number of open file descriptors on macOS" - NOFILE=20480 @@ -61,7 +61,7 @@ jobs: if: type = push os: linux dist: xenial - go: 1.12.x + go: 1.13.x env: - ubuntu-ppa git: @@ -75,9 +75,12 @@ jobs: - fakeroot - python-bzrlib - python-paramiko + cache: + directories: + - $HOME/.gobundle script: - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " + - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder " -goversion 1.13.4 -gohash 95dbeab442ee2746b9acf0934c8e2fc26414a0565c008631b04addb8c02e7624 -gobundle $HOME/.gobundle/go.tar.gz # This builder does the Linux Azure uploads - stage: build @@ -85,7 +88,7 @@ jobs: os: linux dist: xenial sudo: required - go: 1.12.x + go: 1.13.x env: - azure-linux git: @@ -121,7 +124,7 @@ jobs: dist: xenial services: - docker - go: 1.12.x + go: 1.13.x env: - azure-linux-mips git: @@ -167,7 +170,7 @@ jobs: git: submodules: false # avoid cloning ethereum/tests before_install: - - curl https://dl.google.com/go/go1.12.linux-amd64.tar.gz | tar -xz + - curl https://dl.google.com/go/go1.13.linux-amd64.tar.gz | tar -xz - export PATH=`pwd`/go/bin:$PATH - export GOROOT=`pwd`/go - export GOPATH=$HOME/go @@ -185,7 +188,7 @@ jobs: - stage: build if: type = push os: osx - go: 1.12.x + go: 1.13.x env: - azure-osx - azure-ios @@ -216,7 +219,7 @@ jobs: if: type = cron os: linux dist: xenial - go: 1.12.x + go: 1.13.x env: - azure-purge git: diff --git a/vendor/github.com/ethereum/go-ethereum/Dockerfile b/vendor/github.com/ethereum/go-ethereum/Dockerfile index c766576a8f..114e762058 100644 --- a/vendor/github.com/ethereum/go-ethereum/Dockerfile +++ b/vendor/github.com/ethereum/go-ethereum/Dockerfile @@ -1,5 +1,5 @@ # Build Geth in a stock Go builder container -FROM golang:1.12-alpine as builder +FROM golang:1.13-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git @@ -12,5 +12,5 @@ FROM alpine:latest RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/ -EXPOSE 8545 8546 30303 30303/udp +EXPOSE 8545 8546 8547 30303 30303/udp ENTRYPOINT ["geth"] diff --git a/vendor/github.com/ethereum/go-ethereum/Dockerfile.alltools b/vendor/github.com/ethereum/go-ethereum/Dockerfile.alltools index a4adba9d5b..2f661ba01c 100644 --- a/vendor/github.com/ethereum/go-ethereum/Dockerfile.alltools +++ b/vendor/github.com/ethereum/go-ethereum/Dockerfile.alltools @@ -1,5 +1,5 @@ # Build Geth in a stock Go builder container -FROM golang:1.12-alpine as builder +FROM golang:1.13-alpine as builder RUN apk add --no-cache make gcc musl-dev linux-headers git @@ -12,4 +12,4 @@ FROM alpine:latest RUN apk add --no-cache ca-certificates COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/ -EXPOSE 8545 8546 30303 30303/udp +EXPOSE 8545 8546 8547 30303 30303/udp diff --git a/vendor/github.com/ethereum/go-ethereum/Makefile b/vendor/github.com/ethereum/go-ethereum/Makefile index 4bf52f5c96..5d4a82de83 100644 --- a/vendor/github.com/ethereum/go-ethereum/Makefile +++ b/vendor/github.com/ethereum/go-ethereum/Makefile @@ -8,7 +8,7 @@ .PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64 .PHONY: geth-windows geth-windows-386 geth-windows-amd64 -GOBIN = $(shell pwd)/build/bin +GOBIN = ./build/bin GO ?= latest geth: diff --git a/vendor/github.com/ethereum/go-ethereum/README.md b/vendor/github.com/ethereum/go-ethereum/README.md index fd25941543..92a7125b49 100644 --- a/vendor/github.com/ethereum/go-ethereum/README.md +++ b/vendor/github.com/ethereum/go-ethereum/README.md @@ -98,7 +98,7 @@ Specifying the `--testnet` flag, however, will reconfigure your `geth` instance this. * Instead of connecting the main Ethereum network, the client will connect to the test network, which uses different P2P bootnodes, different network IDs and genesis states. - + *Note: Although there are some internal protective measures to prevent transactions from crossing over between the main network and test network, you should make sure to always use separate accounts for play-money and real-money. Unless you manually move @@ -210,10 +210,14 @@ aware of and agree upon. This consists of a small JSON file (e.g. call it `genes ```json { "config": { - "chainId": 0, + "chainId": , "homesteadBlock": 0, + "eip150Block": 0, "eip155Block": 0, - "eip158Block": 0 + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0 }, "alloc": {}, "coinbase": "0x0000000000000000000000000000000000000000", @@ -229,8 +233,8 @@ aware of and agree upon. This consists of a small JSON file (e.g. call it `genes The above fields should be fine for most purposes, although we'd recommend changing the `nonce` to some random value so you prevent unknown remote nodes from being able -to connect to you. If you'd like to pre-fund some accounts for easier testing, you can -populate the `alloc` field with account configs: +to connect to you. If you'd like to pre-fund some accounts for easier testing, create +the accounts and populate the `alloc` field with their addresses. ```json "alloc": { @@ -299,7 +303,7 @@ ones either). To start a `geth` instance for mining, run it with all your usual by: ```shell -$ geth --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000 +$ geth --mine --miner.threads=1 --etherbase=0x0000000000000000000000000000000000000000 ``` Which will start mining blocks and transactions on a single CPU thread, crediting all diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go index 7831a5ed33..603e956b9d 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/abi.go @@ -75,9 +75,6 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { // Unpack output in v according to the abi specification func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) { - if len(data) == 0 { - return fmt.Errorf("abi: unmarshalling empty output") - } // since there can't be naming collisions with contracts and events, // we need to decide whether we're calling a method or an event if method, ok := abi.Methods[name]; ok { @@ -94,9 +91,6 @@ func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) { // UnpackIntoMap unpacks a log into the provided map[string]interface{} func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) { - if len(data) == 0 { - return fmt.Errorf("abi: unmarshalling empty output") - } // since there can't be naming collisions with contracts and events, // we need to decide whether we're calling a method or an event if method, ok := abi.Methods[name]; ok { diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go index 4dae586535..f8ec11b9fa 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/argument.go @@ -34,10 +34,11 @@ type Argument struct { type Arguments []Argument type ArgumentMarshaling struct { - Name string - Type string - Components []ArgumentMarshaling - Indexed bool + Name string + Type string + InternalType string + Components []ArgumentMarshaling + Indexed bool } // UnmarshalJSON implements json.Unmarshaler interface @@ -48,7 +49,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error { return fmt.Errorf("argument json err: %v", err) } - argument.Type, err = NewType(arg.Type, arg.Components) + argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components) if err != nil { return err } @@ -88,6 +89,13 @@ func (arguments Arguments) isTuple() bool { // Unpack performs the operation hexdata -> Go format func (arguments Arguments) Unpack(v interface{}, data []byte) error { + if len(data) == 0 { + if len(arguments) != 0 { + return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + } else { + return nil // Nothing to unmarshal, return + } + } // make sure the passed value is arguments pointer if reflect.Ptr != reflect.ValueOf(v).Kind() { return fmt.Errorf("abi: Unpack(non-pointer %T)", v) @@ -104,11 +112,17 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error { // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error { + if len(data) == 0 { + if len(arguments) != 0 { + return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + } else { + return nil // Nothing to unmarshal, return + } + } marshalledValues, err := arguments.UnpackValues(data) if err != nil { return err } - return arguments.unpackIntoMap(v, marshalledValues) } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go index f74a0af211..499b4bda07 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/base.go @@ -218,7 +218,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } } // If the contract surely has code (or code is not needed), estimate the transaction - msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input} + msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input} gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) if err != nil { return nil, fmt.Errorf("failed to estimate gas needed: %v", err) diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go index dc51e2a7ec..7bda997a61 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/bind.go @@ -86,7 +86,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] if input.Name == "" { normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) } - if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist { + if hasStruct(input.Type) { bindStructType[lang](input.Type, structs) } } @@ -96,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] if output.Name != "" { normalized.Outputs[j].Name = capitalise(output.Name) } - if _, exist := structs[output.Type.String()]; output.Type.T == abi.TupleTy && !exist { + if hasStruct(output.Type) { bindStructType[lang](output.Type, structs) } } @@ -119,14 +119,11 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] normalized.Inputs = make([]abi.Argument, len(original.Inputs)) copy(normalized.Inputs, original.Inputs) for j, input := range normalized.Inputs { - // Indexed fields are input, non-indexed ones are outputs - if input.Indexed { - if input.Name == "" { - normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) - } - if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist { - bindStructType[lang](input.Type, structs) - } + if input.Name == "" { + normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) + } + if hasStruct(input.Type) { + bindStructType[lang](input.Type, structs) } } // Append the event to the accumulator list @@ -244,7 +241,7 @@ func bindBasicTypeGo(kind abi.Type) string { func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { switch kind.T { case abi.TupleTy: - return structs[kind.String()].Name + return structs[kind.TupleRawName+kind.String()].Name case abi.ArrayTy: return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) case abi.SliceTy: @@ -321,7 +318,7 @@ func pluralizeJavaType(typ string) string { func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { switch kind.T { case abi.TupleTy: - return structs[kind.String()].Name + return structs[kind.TupleRawName+kind.String()].Name case abi.ArrayTy, abi.SliceTy: return pluralizeJavaType(bindTypeJava(*kind.Elem, structs)) default: @@ -340,6 +337,13 @@ var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) // funcionality as for simple types, but dynamic types get converted to hashes. func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { bound := bindTypeGo(kind, structs) + + // todo(rjl493456442) according solidity documentation, indexed event + // parameters that are not value types i.e. arrays and structs are not + // stored directly but instead a keccak256-hash of an encoding is stored. + // + // We only convert stringS and bytes to hash, still need to deal with + // array(both fixed-size and dynamic-size) and struct. if bound == "string" || bound == "[]byte" { bound = "common.Hash" } @@ -350,6 +354,13 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { // funcionality as for simple types, but dynamic types get converted to hashes. func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { bound := bindTypeJava(kind, structs) + + // todo(rjl493456442) according solidity documentation, indexed event + // parameters that are not value types i.e. arrays and structs are not + // stored directly but instead a keccak256-hash of an encoding is stored. + // + // We only convert stringS and bytes to hash, still need to deal with + // array(both fixed-size and dynamic-size) and struct. if bound == "String" || bound == "byte[]" { bound = "Hash" } @@ -369,7 +380,14 @@ var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { switch kind.T { case abi.TupleTy: - if s, exist := structs[kind.String()]; exist { + // We compose raw struct name and canonical parameter expression + // together here. The reason is before solidity v0.5.11, kind.TupleRawName + // is empty, so we use canonical parameter expression to distinguish + // different struct definition. From the consideration of backward + // compatibility, we concat these two together so that if kind.TupleRawName + // is not empty, it can have unique id. + id := kind.TupleRawName + kind.String() + if s, exist := structs[id]; exist { return s.Name } var fields []*tmplField @@ -377,8 +395,11 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { field := bindStructTypeGo(*elem, structs) fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem}) } - name := fmt.Sprintf("Struct%d", len(structs)) - structs[kind.String()] = &tmplStruct{ + name := kind.TupleRawName + if name == "" { + name = fmt.Sprintf("Struct%d", len(structs)) + } + structs[id] = &tmplStruct{ Name: name, Fields: fields, } @@ -398,7 +419,14 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { switch kind.T { case abi.TupleTy: - if s, exist := structs[kind.String()]; exist { + // We compose raw struct name and canonical parameter expression + // together here. The reason is before solidity v0.5.11, kind.TupleRawName + // is empty, so we use canonical parameter expression to distinguish + // different struct definition. From the consideration of backward + // compatibility, we concat these two together so that if kind.TupleRawName + // is not empty, it can have unique id. + id := kind.TupleRawName + kind.String() + if s, exist := structs[id]; exist { return s.Name } var fields []*tmplField @@ -406,8 +434,11 @@ func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { field := bindStructTypeJava(*elem, structs) fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem}) } - name := fmt.Sprintf("Class%d", len(structs)) - structs[kind.String()] = &tmplStruct{ + name := kind.TupleRawName + if name == "" { + name = fmt.Sprintf("Class%d", len(structs)) + } + structs[id] = &tmplStruct{ Name: name, Fields: fields, } @@ -497,6 +528,21 @@ func structured(args abi.Arguments) bool { return true } +// hasStruct returns an indicator whether the given type is struct, struct slice +// or struct array. +func hasStruct(t abi.Type) bool { + switch t.T { + case abi.SliceTy: + return hasStruct(*t.Elem) + case abi.ArrayTy: + return hasStruct(*t.Elem) + case abi.TupleTy: + return true + default: + return false + } +} + // resolveArgName converts a raw argument representation into a user friendly format. func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string { var ( @@ -512,7 +558,7 @@ loop: case abi.ArrayTy: prefix += fmt.Sprintf("[%d]", typ.Size) default: - embedded = typ.String() + embedded = typ.TupleRawName + typ.String() break loop } typ = typ.Elem diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go index 4ec65474b0..5b35b1badc 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/template.go @@ -65,7 +65,7 @@ type tmplField struct { // tmplStruct is a wrapper around an abi.tuple contains a auto-generated // struct name. type tmplStruct struct { - Name string // Auto-generated struct name(We can't obtain the raw struct name through abi) + Name string // Auto-generated struct name(before solidity v0.5.11) or raw name. Fields []*tmplField // Struct fields definition depends on the binding language. } @@ -483,7 +483,7 @@ var ( // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}. // - // Solidity: {{.Original.String}} + // Solidity: {{formatevent .Original $structs}} func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) { event := new({{$contract.Type}}{{.Normalized.Name}}) if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/topics.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/topics.go index c7657b4a41..e27fa54842 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/topics.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/topics.go @@ -80,15 +80,19 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) { copy(topic[:], hash[:]) default: + // todo(rjl493456442) according solidity documentation, indexed event + // parameters that are not value types i.e. arrays and structs are not + // stored directly but instead a keccak256-hash of an encoding is stored. + // + // We only convert stringS and bytes to hash, still need to deal with + // array(both fixed-size and dynamic-size) and struct. + // Attempt to generate the topic from funky types val := reflect.ValueOf(rule) - switch { - // static byte array case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val) - default: return nil, fmt.Errorf("unsupported indexed type: %T", rule) } @@ -162,6 +166,7 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er default: // Ran out of plain primitive types, try custom types + switch field.Type() { case reflectHash: // Also covers all dynamic types field.Set(reflect.ValueOf(topics[0])) @@ -178,11 +183,9 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er default: // Ran out of custom types, try the crazies switch { - // static byte array case arg.Type.T == abi.FixedBytesTy: reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size])) - default: return fmt.Errorf("unsupported indexed type: %v", arg.Type) } diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go index 597d314392..4792283ee8 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/abi/type.go @@ -53,6 +53,7 @@ type Type struct { stringKind string // holds the unparsed string for deriving signatures // Tuple relative fields + TupleRawName string // Raw struct name defined in source code, may be empty. TupleElems []*Type // Type information of all tuple fields TupleRawNames []string // Raw field name of all tuple fields } @@ -63,7 +64,7 @@ var ( ) // NewType creates a new reflection type of abi type given in t. -func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) { +func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) { // check that array brackets are equal if they exist if strings.Count(t, "[") != strings.Count(t, "]") { return Type{}, fmt.Errorf("invalid arg type in abi") @@ -73,9 +74,14 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) { // if there are brackets, get ready to go into slice/array mode and // recursively create the type if strings.Count(t, "[") != 0 { - i := strings.LastIndex(t, "[") + // Note internalType can be empty here. + subInternal := internalType + if i := strings.LastIndex(internalType, "["); i != -1 { + subInternal = subInternal[:i] + } // recursively embed the type - embeddedType, err := NewType(t[:i], components) + i := strings.LastIndex(t, "[") + embeddedType, err := NewType(t[:i], subInternal, components) if err != nil { return Type{}, err } @@ -173,7 +179,7 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) { ) expression += "(" for idx, c := range components { - cType, err := NewType(c.Type, c.Components) + cType, err := NewType(c.Type, c.InternalType, c.Components) if err != nil { return Type{}, err } @@ -199,6 +205,17 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) { typ.TupleRawNames = names typ.T = TupleTy typ.stringKind = expression + + const structPrefix = "struct " + // After solidity 0.5.10, a new field of abi "internalType" + // is introduced. From that we can obtain the struct name + // user defined in the source code. + if internalType != "" && strings.HasPrefix(internalType, structPrefix) { + // Foo.Bar type definition is not allowed in golang, + // convert the format to FooBar + typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1) + } + case "function": typ.Kind = reflect.Array typ.T = FunctionTy diff --git a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger.go b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger.go index c30903b5b7..17ca9223ff 100644 --- a/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger.go +++ b/vendor/github.com/ethereum/go-ethereum/accounts/usbwallet/ledger.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" ) @@ -341,7 +342,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction op = ledgerP1ContTransactionData } // Extract the Ethereum signature and do a sanity validation - if len(reply) != 65 { + if len(reply) != crypto.SignatureLength { return common.Address{}, nil, errors.New("reply lacks signature") } signature := append(reply[1:], reply[0]) diff --git a/vendor/github.com/ethereum/go-ethereum/appveyor.yml b/vendor/github.com/ethereum/go-ethereum/appveyor.yml index 8f840c190e..0f230bac14 100644 --- a/vendor/github.com/ethereum/go-ethereum/appveyor.yml +++ b/vendor/github.com/ethereum/go-ethereum/appveyor.yml @@ -23,8 +23,8 @@ environment: install: - git submodule update --init - rmdir C:\go /s /q - - appveyor DownloadFile https://dl.google.com/go/go1.12.7.windows-%GETH_ARCH%.zip - - 7z x go1.12.7.windows-%GETH_ARCH%.zip -y -oC:\ > NUL + - appveyor DownloadFile https://dl.google.com/go/go1.13.4.windows-%GETH_ARCH%.zip + - 7z x go1.13.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - go version - gcc --version diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go index e5bf8724c1..66ebf9ab04 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/customflags.go @@ -20,7 +20,6 @@ import ( "encoding" "errors" "flag" - "fmt" "math/big" "os" "os/user" @@ -34,33 +33,44 @@ import ( // Custom type which is registered in the flags library which cli uses for // argument parsing. This allows us to expand Value to an absolute path when // the argument is parsed -type DirectoryString struct { - Value string -} +type DirectoryString string -func (self *DirectoryString) String() string { - return self.Value +func (s *DirectoryString) String() string { + return string(*s) } -func (self *DirectoryString) Set(value string) error { - self.Value = expandPath(value) +func (s *DirectoryString) Set(value string) error { + *s = DirectoryString(expandPath(value)) return nil } // Custom cli.Flag type which expand the received string to an absolute path. // e.g. ~/.ethereum -> /home/username/.ethereum type DirectoryFlag struct { - Name string - Value DirectoryString - Usage string + Name string + Value DirectoryString + Usage string + EnvVar string } -func (self DirectoryFlag) String() string { - fmtString := "%s %v\t%v" - if len(self.Value.Value) > 0 { - fmtString = "%s \"%v\"\t%v" - } - return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage) +func (f DirectoryFlag) String() string { + return cli.FlagStringer(f) +} + +// called by cli library, grabs variable from environment (if in env) +// and adds variable to flag set for parsing. +func (f DirectoryFlag) Apply(set *flag.FlagSet) { + eachName(f.Name, func(name string) { + set.Var(&f.Value, f.Name, f.Usage) + }) +} + +func (f DirectoryFlag) GetName() string { + return f.Name +} + +func (f *DirectoryFlag) Set(value string) { + f.Value.Set(value) } func eachName(longName string, fn func(string)) { @@ -71,14 +81,6 @@ func eachName(longName string, fn func(string)) { } } -// called by cli library, grabs variable from environment (if in env) -// and adds variable to flag set for parsing. -func (self DirectoryFlag) Apply(set *flag.FlagSet) { - eachName(self.Name, func(name string) { - set.Var(&self.Value, self.Name, self.Usage) - }) -} - type TextMarshaler interface { encoding.TextMarshaler encoding.TextUnmarshaler @@ -103,9 +105,10 @@ func (v textMarshalerVal) Set(s string) error { // TextMarshalerFlag wraps a TextMarshaler value. type TextMarshalerFlag struct { - Name string - Value TextMarshaler - Usage string + Name string + Value TextMarshaler + Usage string + EnvVar string } func (f TextMarshalerFlag) GetName() string { @@ -113,7 +116,7 @@ func (f TextMarshalerFlag) GetName() string { } func (f TextMarshalerFlag) String() string { - return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage) + return cli.FlagStringer(f) } func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { @@ -134,9 +137,10 @@ func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler { // BigFlag is a command line flag that accepts 256 bit big integers in decimal or // hexadecimal syntax. type BigFlag struct { - Name string - Value *big.Int - Usage string + Name string + Value *big.Int + Usage string + EnvVar string } // bigValue turns *big.Int into a flag.Value @@ -163,11 +167,7 @@ func (f BigFlag) GetName() string { } func (f BigFlag) String() string { - fmtString := "%s %v\t%v" - if f.Value != nil { - fmtString = "%s \"%v\"\t%v" - } - return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage) + return cli.FlagStringer(f) } func (f BigFlag) Apply(set *flag.FlagSet) { @@ -207,14 +207,6 @@ func prefixedNames(fullName string) (prefixed string) { return } -func (self DirectoryFlag) GetName() string { - return self.Name -} - -func (self *DirectoryFlag) Set(value string) { - self.Value.Value = value -} - // Expands a file path // 1. replace tilde with users home dir // 2. expands embedded environment variables diff --git a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go index 7e28dff79a..42424bcb52 100644 --- a/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go +++ b/vendor/github.com/ethereum/go-ethereum/cmd/utils/flags.go @@ -21,12 +21,15 @@ import ( "crypto/ecdsa" "errors" "fmt" + "io" "io/ioutil" "math/big" "os" "path/filepath" "strconv" "strings" + "text/tabwriter" + "text/template" "time" "github.com/ethereum/go-ethereum/accounts" @@ -90,8 +93,8 @@ GLOBAL OPTIONS: {{range .Flags}}{{.}} {{end}}{{end}} ` - cli.CommandHelpTemplate = CommandHelpTemplate + cli.HelpPrinter = printHelp } // NewApp creates an app with sane defaults. @@ -105,6 +108,17 @@ func NewApp(gitCommit, gitDate, usage string) *cli.App { return app } +func printHelp(out io.Writer, templ string, data interface{}) { + funcMap := template.FuncMap{"join": strings.Join} + t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) + w := tabwriter.NewWriter(out, 38, 8, 2, ' ', 0) + err := t.Execute(w, data) + if err != nil { + panic(err) + } + w.Flush() +} + // These are all the command line flags we support. // If you add to this list, please remember to include the // flag in the appropriate command definition. @@ -117,7 +131,7 @@ var ( DataDirFlag = DirectoryFlag{ Name: "datadir", Usage: "Data directory for the databases and keystore", - Value: DirectoryString{node.DefaultDataDir()}, + Value: DirectoryString(node.DefaultDataDir()), } AncientFlag = DirectoryFlag{ Name: "datadir.ancient", @@ -168,7 +182,7 @@ var ( DocRootFlag = DirectoryFlag{ Name: "docroot", Usage: "Document Root for HTTPClient file scheme", - Value: DirectoryString{homeDir()}, + Value: DirectoryString(homeDir()), } ExitWhenSyncedFlag = cli.BoolFlag{ Name: "exitwhensynced", @@ -209,6 +223,10 @@ var ( Name: "whitelist", Usage: "Comma separated block number-to-hash mappings to enforce (=)", } + OverrideIstanbulFlag = cli.Uint64Flag{ + Name: "override.istanbul", + Usage: "Manually specify Istanbul fork-block, overriding the bundled setting", + } // Light server and client settings LightLegacyServFlag = cli.IntFlag{ // Deprecated in favor of light.serve, remove in 2021 Name: "lightserv", @@ -291,8 +309,8 @@ var ( } EthashDatasetDirFlag = DirectoryFlag{ Name: "ethash.dagdir", - Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", - Value: DirectoryString{eth.DefaultConfig.Ethash.DatasetDir}, + Usage: "Directory to store the ethash mining DAGs", + Value: DirectoryString(eth.DefaultConfig.Ethash.DatasetDir), } EthashDatasetsInMemoryFlag = cli.IntFlag{ Name: "ethash.dagsinmem", @@ -1091,6 +1109,11 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { if ctx.GlobalIsSet(LightMaxPeersFlag.Name) { lightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name) } + if lightClient && !ctx.GlobalIsSet(LightLegacyPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { + // dynamic default - for clients we use 1/10th of the default for servers + lightPeers /= 10 + } + if ctx.GlobalIsSet(MaxPeersFlag.Name) { cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) if lightServer && !ctx.GlobalIsSet(LightLegacyPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) { @@ -1430,9 +1453,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) } - cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive" - cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name) - + if ctx.GlobalIsSet(GCModeFlag.Name) { + cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive" + } + if ctx.GlobalIsSet(CacheNoPrefetchFlag.Name) { + cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name) + } if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) { cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100 } diff --git a/vendor/github.com/ethereum/go-ethereum/common/bytes.go b/vendor/github.com/ethereum/go-ethereum/common/bytes.go index c82e616241..fa457b92cf 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/bytes.go +++ b/vendor/github.com/ethereum/go-ethereum/common/bytes.go @@ -43,10 +43,8 @@ func ToHexArray(b [][]byte) []string { // FromHex returns the bytes represented by the hexadecimal string s. // s may be prefixed with "0x". func FromHex(s string) []byte { - if len(s) > 1 { - if s[0:2] == "0x" || s[0:2] == "0X" { - s = s[2:] - } + if has0xPrefix(s) { + s = s[2:] } if len(s)%2 == 1 { s = "0" + s @@ -65,8 +63,8 @@ func CopyBytes(b []byte) (copiedBytes []byte) { return } -// hasHexPrefix validates str begins with '0x' or '0X'. -func hasHexPrefix(str string) bool { +// has0xPrefix validates str begins with '0x' or '0X'. +func has0xPrefix(str string) bool { return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') } @@ -136,3 +134,14 @@ func LeftPadBytes(slice []byte, l int) []byte { return padded } + +// TrimLeftZeroes returns a subslice of s without leading zeroes +func TrimLeftZeroes(s []byte) []byte { + idx := 0 + for ; idx < len(s); idx++ { + if s[idx] != 0 { + break + } + } + return s[idx:] +} diff --git a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go index 0c941082f3..d0e0cd78be 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go +++ b/vendor/github.com/ethereum/go-ethereum/common/mclock/mclock.go @@ -36,47 +36,39 @@ func (t AbsTime) Add(d time.Duration) AbsTime { return t + AbsTime(d) } -// Clock interface makes it possible to replace the monotonic system clock with +// The Clock interface makes it possible to replace the monotonic system clock with // a simulated clock. type Clock interface { Now() AbsTime Sleep(time.Duration) After(time.Duration) <-chan time.Time - AfterFunc(d time.Duration, f func()) Event + AfterFunc(d time.Duration, f func()) Timer } -// Event represents a cancellable event returned by AfterFunc -type Event interface { - Cancel() bool +// Timer represents a cancellable event returned by AfterFunc +type Timer interface { + Stop() bool } // System implements Clock using the system clock. type System struct{} -// Now implements Clock. +// Now returns the current monotonic time. func (System) Now() AbsTime { return AbsTime(monotime.Now()) } -// Sleep implements Clock. +// Sleep blocks for the given duration. func (System) Sleep(d time.Duration) { time.Sleep(d) } -// After implements Clock. +// After returns a channel which receives the current time after d has elapsed. func (System) After(d time.Duration) <-chan time.Time { return time.After(d) } -// AfterFunc implements Clock. -func (System) AfterFunc(d time.Duration, f func()) Event { - return (*SystemEvent)(time.AfterFunc(d, f)) -} - -// SystemEvent implements Event using time.Timer. -type SystemEvent time.Timer - -// Cancel implements Event. -func (e *SystemEvent) Cancel() bool { - return (*time.Timer)(e).Stop() +// AfterFunc runs f on a new goroutine after the duration has elapsed. +func (System) AfterFunc(d time.Duration, f func()) Timer { + return time.AfterFunc(d, f) } diff --git a/vendor/github.com/ethereum/go-ethereum/common/mclock/simclock.go b/vendor/github.com/ethereum/go-ethereum/common/mclock/simclock.go index af0f71c430..4d351252ff 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/mclock/simclock.go +++ b/vendor/github.com/ethereum/go-ethereum/common/mclock/simclock.go @@ -32,22 +32,17 @@ import ( // the timeout using a channel or semaphore. type Simulated struct { now AbsTime - scheduled []event + scheduled []*simTimer mu sync.RWMutex cond *sync.Cond lastId uint64 } -type event struct { +// simTimer implements Timer on the virtual clock. +type simTimer struct { do func() at AbsTime id uint64 -} - -// SimulatedEvent implements Event for a virtual clock. -type SimulatedEvent struct { - at AbsTime - id uint64 s *Simulated } @@ -75,6 +70,7 @@ func (s *Simulated) Run(d time.Duration) { } } +// ActiveTimers returns the number of timers that haven't fired. func (s *Simulated) ActiveTimers() int { s.mu.RLock() defer s.mu.RUnlock() @@ -82,6 +78,7 @@ func (s *Simulated) ActiveTimers() int { return len(s.scheduled) } +// WaitForTimers waits until the clock has at least n scheduled timers. func (s *Simulated) WaitForTimers(n int) { s.mu.Lock() defer s.mu.Unlock() @@ -92,7 +89,7 @@ func (s *Simulated) WaitForTimers(n int) { } } -// Now implements Clock. +// Now returns the current virtual time. func (s *Simulated) Now() AbsTime { s.mu.RLock() defer s.mu.RUnlock() @@ -100,12 +97,13 @@ func (s *Simulated) Now() AbsTime { return s.now } -// Sleep implements Clock. +// Sleep blocks until the clock has advanced by d. func (s *Simulated) Sleep(d time.Duration) { <-s.After(d) } -// After implements Clock. +// After returns a channel which receives the current time after the clock +// has advanced by d. func (s *Simulated) After(d time.Duration) <-chan time.Time { after := make(chan time.Time, 1) s.AfterFunc(d, func() { @@ -114,8 +112,9 @@ func (s *Simulated) After(d time.Duration) <-chan time.Time { return after } -// AfterFunc implements Clock. -func (s *Simulated) AfterFunc(d time.Duration, do func()) Event { +// AfterFunc runs fn after the clock has advanced by d. Unlike with the system +// clock, fn runs on the goroutine that calls Run. +func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer { s.mu.Lock() defer s.mu.Unlock() s.init() @@ -133,44 +132,31 @@ func (s *Simulated) AfterFunc(d time.Duration, do func()) Event { l = m + 1 } } - s.scheduled = append(s.scheduled, event{}) + ev := &simTimer{do: fn, at: at, s: s} + s.scheduled = append(s.scheduled, nil) copy(s.scheduled[l+1:], s.scheduled[l:ll]) - e := event{do: do, at: at, id: id} - s.scheduled[l] = e + s.scheduled[l] = ev s.cond.Broadcast() - return &SimulatedEvent{at: at, id: id, s: s} -} - -func (s *Simulated) init() { - if s.cond == nil { - s.cond = sync.NewCond(&s.mu) - } + return ev } -// Cancel implements Event. -func (e *SimulatedEvent) Cancel() bool { - s := e.s +func (ev *simTimer) Stop() bool { + s := ev.s s.mu.Lock() defer s.mu.Unlock() - l, h := 0, len(s.scheduled) - ll := h - for l != h { - m := (l + h) / 2 - if e.id == s.scheduled[m].id { - l = m - break - } - if (e.at < s.scheduled[m].at) || ((e.at == s.scheduled[m].at) && (e.id < s.scheduled[m].id)) { - h = m - } else { - l = m + 1 + for i := 0; i < len(s.scheduled); i++ { + if s.scheduled[i] == ev { + s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...) + s.cond.Broadcast() + return true } } - if l >= ll || s.scheduled[l].id != e.id { - return false + return false +} + +func (s *Simulated) init() { + if s.cond == nil { + s.cond = sync.NewCond(&s.mu) } - copy(s.scheduled[l:ll-1], s.scheduled[l+1:]) - s.scheduled = s.scheduled[:ll-1] - return true } diff --git a/vendor/github.com/ethereum/go-ethereum/common/types.go b/vendor/github.com/ethereum/go-ethereum/common/types.go index 98c83edd4f..8ca51a05f8 100644 --- a/vendor/github.com/ethereum/go-ethereum/common/types.go +++ b/vendor/github.com/ethereum/go-ethereum/common/types.go @@ -149,7 +149,7 @@ func (h *Hash) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: - *h = HexToHash(input) + err = h.UnmarshalText([]byte(input)) default: err = fmt.Errorf("Unexpected type for Bytes32: %v", input) } @@ -193,7 +193,7 @@ func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } // IsHexAddress verifies whether a string can represent a valid hex-encoded // Ethereum address or not. func IsHexAddress(s string) bool { - if hasHexPrefix(s) { + if has0xPrefix(s) { s = s[2:] } return len(s) == 2*AddressLength && isHex(s) @@ -288,7 +288,7 @@ func (a *Address) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: - *a = HexToAddress(input) + err = a.UnmarshalText([]byte(input)) default: err = fmt.Errorf("Unexpected type for Address: %v", input) } diff --git a/vendor/github.com/ethereum/go-ethereum/consensus/clique/clique.go b/vendor/github.com/ethereum/go-ethereum/consensus/clique/clique.go index 084009a066..100c205292 100644 --- a/vendor/github.com/ethereum/go-ethereum/consensus/clique/clique.go +++ b/vendor/github.com/ethereum/go-ethereum/consensus/clique/clique.go @@ -55,8 +55,8 @@ const ( var ( epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes - extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity - extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal + extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity + extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer. @@ -311,7 +311,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type if number == 0 { return nil } - // Ensure that the block's timestamp isn't too close to it's parent + // Ensure that the block's timestamp isn't too close to its parent var parent *types.Header if len(parents) > 0 { parent = parents[len(parents)-1] @@ -522,7 +522,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro // Set the correct difficulty header.Difficulty = CalcDifficulty(snap, c.signer) - // Ensure the extra data has all it's components + // Ensure the extra data has all its components if len(header.Extra) < extraVanity { header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) } @@ -728,7 +728,7 @@ func encodeSigHeader(w io.Writer, header *types.Header) { header.GasLimit, header.GasUsed, header.Time, - header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short + header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short header.MixDigest, header.Nonce, }) diff --git a/vendor/github.com/ethereum/go-ethereum/consensus/errors.go b/vendor/github.com/ethereum/go-ethereum/consensus/errors.go index a005c5f63d..ac5242fb54 100644 --- a/vendor/github.com/ethereum/go-ethereum/consensus/errors.go +++ b/vendor/github.com/ethereum/go-ethereum/consensus/errors.go @@ -31,7 +31,7 @@ var ( // to the current node. ErrFutureBlock = errors.New("block in the future") - // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's + // ErrInvalidNumber is returned if a block's number doesn't equal its parent's // plus one. ErrInvalidNumber = errors.New("invalid block number") ) diff --git a/vendor/github.com/ethereum/go-ethereum/consensus/ethash/consensus.go b/vendor/github.com/ethereum/go-ethereum/consensus/ethash/consensus.go index d271518f4f..3cff2d9fe5 100644 --- a/vendor/github.com/ethereum/go-ethereum/consensus/ethash/consensus.go +++ b/vendor/github.com/ethereum/go-ethereum/consensus/ethash/consensus.go @@ -86,7 +86,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He if ethash.config.PowMode == ModeFullFake { return nil } - // Short circuit if the header is known, or it's parent not + // Short circuit if the header is known, or its parent not number := header.Number.Uint64() if chain.GetHeader(header.Hash(), number) != nil { return nil @@ -252,7 +252,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * if header.Time <= parent.Time { return errZeroBlockTime } - // Verify the block's difficulty based in it's timestamp and parent's difficulty + // Verify the block's difficulty based in its timestamp and parent's difficulty expected := ethash.CalcDifficulty(chain, header.Time, parent) if expected.Cmp(header.Difficulty) != 0 { diff --git a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go index 59be355895..9fb02b1482 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/blockchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/blockchain.go @@ -64,6 +64,8 @@ var ( blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil) blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil) blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil) + blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil) + blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil) blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil) blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil) @@ -75,6 +77,7 @@ const ( bodyCacheLimit = 256 blockCacheLimit = 256 receiptsCacheLimit = 32 + txLookupCacheLimit = 1024 maxFutureBlocks = 256 maxTimeFutureBlocks = 30 badBlockLimit = 10 @@ -155,6 +158,7 @@ type BlockChain struct { bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format receiptsCache *lru.Cache // Cache for the most recent receipts per block blockCache *lru.Cache // Cache for the most recent entire blocks + txLookupCache *lru.Cache // Cache for the most recent transaction lookup data. futureBlocks *lru.Cache // future blocks are blocks added for later processing quit chan struct{} // blockchain quit channel @@ -189,6 +193,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par bodyRLPCache, _ := lru.New(bodyCacheLimit) receiptsCache, _ := lru.New(receiptsCacheLimit) blockCache, _ := lru.New(blockCacheLimit) + txLookupCache, _ := lru.New(txLookupCacheLimit) futureBlocks, _ := lru.New(maxFutureBlocks) badBlocks, _ := lru.New(badBlockLimit) @@ -204,6 +209,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par bodyRLPCache: bodyRLPCache, receiptsCache: receiptsCache, blockCache: blockCache, + txLookupCache: txLookupCache, futureBlocks: futureBlocks, engine: engine, vmConfig: vmConfig, @@ -222,10 +228,16 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par if bc.genesisBlock == nil { return nil, ErrNoGenesis } + + var nilBlock *types.Block + bc.currentBlock.Store(nilBlock) + bc.currentFastBlock.Store(nilBlock) + // Initialize the chain with ancient data if it isn't empty. if bc.empty() { rawdb.InitDatabaseFromFreezer(bc.db) } + if err := bc.loadLastState(); err != nil { return nil, err } @@ -440,6 +452,7 @@ func (bc *BlockChain) SetHead(head uint64) error { bc.bodyRLPCache.Purge() bc.receiptsCache.Purge() bc.blockCache.Purge() + bc.txLookupCache.Purge() bc.futureBlocks.Purge() return bc.loadLastState() @@ -921,6 +934,7 @@ func (bc *BlockChain) truncateAncient(head uint64) error { bc.bodyRLPCache.Purge() bc.receiptsCache.Purge() bc.blockCache.Purge() + bc.txLookupCache.Purge() bc.futureBlocks.Purge() log.Info("Rewind ancient data", "number", head) @@ -1541,6 +1555,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] // Some other error occurred, abort case err != nil: + bc.futureBlocks.Remove(block.Hash()) stats.ignored += len(it.chain) bc.reportBlock(block, nil, err) return it.index, events, coalescedLogs, err @@ -1742,6 +1757,11 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i canonical := bc.GetBlockByNumber(number) if canonical != nil && canonical.Hash() == block.Hash() { // Not a sidechain block, this is a re-import of a canon block which has it's state pruned + + // Collect the TD of the block. Since we know it's a canon one, + // we can get it directly, and not (like further below) use + // the parent and then add the block on top + externTd = bc.GetTd(block.Hash(), block.NumberU64()) continue } if canonical != nil && canonical.Root() == block.Root() { @@ -1922,12 +1942,16 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { } // Ensure the user sees large reorgs if len(oldChain) > 0 && len(newChain) > 0 { - logFn := log.Debug + logFn := log.Info + msg := "Chain reorg detected" if len(oldChain) > 63 { + msg = "Large chain reorg detected" logFn = log.Warn } - logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(), + logFn(msg, "number", commonBlock.Number(), "hash", commonBlock.Hash(), "drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash()) + blockReorgAddMeter.Mark(int64(len(newChain))) + blockReorgDropMeter.Mark(int64(len(oldChain))) } else { log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash()) } @@ -2122,6 +2146,11 @@ func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool { return bc.hc.HasHeader(hash, number) } +// GetCanonicalHash returns the canonical hash for a given block number +func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash { + return bc.hc.GetCanonicalHash(number) +} + // GetBlockHashesFromHash retrieves a number of block hashes starting at a given // hash, fetching towards the genesis block. func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { @@ -2134,9 +2163,6 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com // // Note: ancestor == 0 returns the same block, 1 returns its parent and so on. func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { - bc.chainmu.RLock() - defer bc.chainmu.RUnlock() - return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) } @@ -2146,6 +2172,22 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header { return bc.hc.GetHeaderByNumber(number) } +// GetTransactionLookup retrieves the lookup associate with the given transaction +// hash from the cache or database. +func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry { + // Short circuit if the txlookup already in the cache, retrieve otherwise + if lookup, exist := bc.txLookupCache.Get(hash); exist { + return lookup.(*rawdb.LegacyTxLookupEntry) + } + tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) + if tx == nil { + return nil + } + lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex} + bc.txLookupCache.Add(hash, lookup) + return lookup +} + // Config retrieves the chain's fork configuration. func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig } diff --git a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go index 17f4042116..0b0fcdb4aa 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go +++ b/vendor/github.com/ethereum/go-ethereum/core/chain_makers.go @@ -103,7 +103,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } diff --git a/vendor/github.com/ethereum/go-ethereum/core/forkid/forkid.go b/vendor/github.com/ethereum/go-ethereum/core/forkid/forkid.go index 8c1700879a..1e2d7a7441 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/forkid/forkid.go +++ b/vendor/github.com/ethereum/go-ethereum/core/forkid/forkid.go @@ -50,6 +50,9 @@ type ID struct { Next uint64 // Block number of the next upcoming fork, or 0 if no forks are known } +// Filter is a fork id filter to validate a remotely advertised ID. +type Filter func(id ID) error + // NewID calculates the Ethereum fork ID from the chain config and head. func NewID(chain *core.BlockChain) ID { return newID( @@ -80,9 +83,9 @@ func newID(config *params.ChainConfig, genesis common.Hash, head uint64) ID { return ID{Hash: checksumToBytes(hash), Next: next} } -// NewFilter creates an filter that returns if a fork ID should be rejected or not +// NewFilter creates a filter that returns if a fork ID should be rejected or not // based on the local chain's status. -func NewFilter(chain *core.BlockChain) func(id ID) error { +func NewFilter(chain *core.BlockChain) Filter { return newFilter( chain.Config(), chain.Genesis().Hash(), @@ -92,10 +95,16 @@ func NewFilter(chain *core.BlockChain) func(id ID) error { ) } +// NewStaticFilter creates a filter at block zero. +func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) Filter { + head := func() uint64 { return 0 } + return newFilter(config, genesis, head) +} + // newFilter is the internal version of NewFilter, taking closures as its arguments // instead of a chain. The reason is to allow testing it without having to simulate // an entire blockchain. -func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() uint64) func(id ID) error { +func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() uint64) Filter { // Calculate the all the valid fork hash and fork next combos var ( forks = gatherForks(config) @@ -114,10 +123,13 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui // Create a validator that will filter out incompatible chains return func(id ID) error { // Run the fork checksum validation ruleset: - // 1. If local and remote FORK_CSUM matches, connect. + // 1. If local and remote FORK_CSUM matches, compare local head to FORK_NEXT. // The two nodes are in the same fork state currently. They might know // of differing future forks, but that's not relevant until the fork // triggers (might be postponed, nodes might be updated to match). + // 1a. A remotely announced but remotely not passed block is already passed + // locally, disconnect, since the chains are incompatible. + // 1b. No remotely announced fork; or not yet passed locally, connect. // 2. If the remote FORK_CSUM is a subset of the local past forks and the // remote FORK_NEXT matches with the locally following fork block number, // connect. @@ -139,7 +151,12 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui // Found the first unpassed fork block, check if our current state matches // the remote checksum (rule #1). if sums[i] == id.Hash { - // Yay, fork checksum matched, ignore any upcoming fork + // Fork checksum matched, check if a remote future fork block already passed + // locally without the local node being aware of it (rule #1a). + if id.Next > 0 && head >= id.Next { + return ErrLocalIncompatibleOrStale + } + // Haven't passed locally a remote-only fork, accept the connection (rule #1b). return nil } // The local and remote nodes are in different forks currently, check if the diff --git a/vendor/github.com/ethereum/go-ethereum/core/genesis.go b/vendor/github.com/ethereum/go-ethereum/core/genesis.go index 87bab25201..df0c967980 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/genesis.go +++ b/vendor/github.com/ethereum/go-ethereum/core/genesis.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -151,6 +152,10 @@ func (e *GenesisMismatchError) Error() string { // // The returned chain configuration is never nil. func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { + return SetupGenesisBlockWithOverride(db, genesis, nil) +} + +func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideIstanbul *big.Int) (*params.ChainConfig, common.Hash, error) { if genesis != nil && genesis.Config == nil { return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig } @@ -199,6 +204,12 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig // Get the existing chain configuration. newcfg := genesis.configOrDefault(stored) + if overrideIstanbul != nil { + newcfg.IstanbulBlock = overrideIstanbul + } + if err := newcfg.CheckConfigForkOrder(); err != nil { + return newcfg, common.Hash{}, err + } storedcfg := rawdb.ReadChainConfig(db, stored) if storedcfg == nil { log.Warn("Found genesis block without chain config") @@ -287,6 +298,13 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { if block.Number().Sign() != 0 { return nil, fmt.Errorf("can't commit genesis block with number > 0") } + config := g.Config + if config == nil { + config = params.AllEthashProtocolChanges + } + if err := config.CheckConfigForkOrder(); err != nil { + return nil, err + } rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty) rawdb.WriteBlock(db, block) rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil) @@ -294,11 +312,6 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { rawdb.WriteHeadBlockHash(db, block.Hash()) rawdb.WriteHeadFastBlockHash(db, block.Hash()) rawdb.WriteHeadHeaderHash(db, block.Hash()) - - config := g.Config - if config == nil { - config = params.AllEthashProtocolChanges - } rawdb.WriteChainConfig(db, block.Hash(), config) return block, nil } @@ -377,7 +390,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { // Assemble and return the genesis with the precompiles and faucet pre-funded return &Genesis{ Config: &config, - ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...), + ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...), GasLimit: 6283185, Difficulty: big.NewInt(1), Alloc: map[common.Address]GenesisAccount{ diff --git a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go index 034858f651..4682069cff 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/headerchain.go +++ b/vendor/github.com/ethereum/go-ethereum/core/headerchain.go @@ -349,8 +349,11 @@ func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, ma } for ancestor != 0 { if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash { - number -= ancestor - return rawdb.ReadCanonicalHash(hc.chainDb, number), number + ancestorHash := rawdb.ReadCanonicalHash(hc.chainDb, number-ancestor) + if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash { + number -= ancestor + return ancestorHash, number + } } if *maxNonCanonical == 0 { return common.Hash{}, 0 @@ -445,6 +448,10 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header { return hc.GetHeader(hash, number) } +func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash { + return rawdb.ReadCanonicalHash(hc.chainDb, number) +} + // CurrentHeader retrieves the current head header of the canonical chain. The // header is retrieved from the HeaderChain's internal cache. func (hc *HeaderChain) CurrentHeader() *types.Header { diff --git a/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer.go b/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer.go index 41677fbba2..5497c59d49 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer.go +++ b/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer.go @@ -80,9 +80,9 @@ type freezer struct { func newFreezer(datadir string, namespace string) (*freezer, error) { // Create the initial freezer object var ( - readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) - writeMeter = metrics.NewRegisteredMeter(namespace+"ancient/write", nil) - sizeCounter = metrics.NewRegisteredCounter(namespace+"ancient/size", nil) + readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) + writeMeter = metrics.NewRegisteredMeter(namespace+"ancient/write", nil) + sizeGauge = metrics.NewRegisteredGauge(namespace+"ancient/size", nil) ) // Ensure the datadir is not a symbolic link if it exists. if info, err := os.Lstat(datadir); !os.IsNotExist(err) { @@ -103,7 +103,7 @@ func newFreezer(datadir string, namespace string) (*freezer, error) { instanceLock: lock, } for name, disableSnappy := range freezerNoSnappy { - table, err := newTable(datadir, name, readMeter, writeMeter, sizeCounter, disableSnappy) + table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, disableSnappy) if err != nil { for _, table := range freezer.tables { table.Close() diff --git a/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer_table.go b/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer_table.go index 61804f1f2b..9fb341f025 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer_table.go +++ b/vendor/github.com/ethereum/go-ethereum/core/rawdb/freezer_table.go @@ -94,18 +94,18 @@ type freezerTable struct { // to count how many historic items have gone missing. itemOffset uint32 // Offset (number of discarded items) - headBytes uint32 // Number of bytes written to the head file - readMeter metrics.Meter // Meter for measuring the effective amount of data read - writeMeter metrics.Meter // Meter for measuring the effective amount of data written - sizeCounter metrics.Counter // Counter for tracking the combined size of all freezer tables + headBytes uint32 // Number of bytes written to the head file + readMeter metrics.Meter // Meter for measuring the effective amount of data read + writeMeter metrics.Meter // Meter for measuring the effective amount of data written + sizeGauge metrics.Gauge // Gauge for tracking the combined size of all freezer tables logger log.Logger // Logger with database path and table name ambedded lock sync.RWMutex // Mutex protecting the data file descriptors } // newTable opens a freezer table with default settings - 2G files -func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeCounter metrics.Counter, disableSnappy bool) (*freezerTable, error) { - return newCustomTable(path, name, readMeter, writeMeter, sizeCounter, 2*1000*1000*1000, disableSnappy) +func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, disableSnappy bool) (*freezerTable, error) { + return newCustomTable(path, name, readMeter, writeMeter, sizeGauge, 2*1000*1000*1000, disableSnappy) } // openFreezerFileForAppend opens a freezer table file and seeks to the end @@ -149,7 +149,7 @@ func truncateFreezerFile(file *os.File, size int64) error { // newCustomTable opens a freezer table, creating the data and index files if they are // non existent. Both files are truncated to the shortest common length to ensure // they don't go out of sync. -func newCustomTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeCounter metrics.Counter, maxFilesize uint32, noCompression bool) (*freezerTable, error) { +func newCustomTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxFilesize uint32, noCompression bool) (*freezerTable, error) { // Ensure the containing directory exists and open the indexEntry file if err := os.MkdirAll(path, 0755); err != nil { return nil, err @@ -172,7 +172,7 @@ func newCustomTable(path string, name string, readMeter metrics.Meter, writeMete files: make(map[uint32]*os.File), readMeter: readMeter, writeMeter: writeMeter, - sizeCounter: sizeCounter, + sizeGauge: sizeGauge, name: name, path: path, logger: log.New("database", path, "table", name), @@ -189,7 +189,7 @@ func newCustomTable(path string, name string, readMeter metrics.Meter, writeMete tab.Close() return nil, err } - tab.sizeCounter.Inc(int64(size)) + tab.sizeGauge.Inc(int64(size)) return tab, nil } @@ -378,7 +378,7 @@ func (t *freezerTable) truncate(items uint64) error { if err != nil { return err } - t.sizeCounter.Dec(int64(oldSize - newSize)) + t.sizeGauge.Dec(int64(oldSize - newSize)) return nil } @@ -510,7 +510,7 @@ func (t *freezerTable) Append(item uint64, blob []byte) error { t.index.Write(idx.marshallBinary()) t.writeMeter.Mark(int64(bLen + indexEntrySize)) - t.sizeCounter.Inc(int64(bLen + indexEntrySize)) + t.sizeGauge.Inc(int64(bLen + indexEntrySize)) atomic.AddUint64(&t.items, 1) return nil diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go index 45ae95a2a9..8680de021f 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/state_object.go @@ -79,9 +79,10 @@ type stateObject struct { trie Trie // storage trie, which becomes non-nil on first access code Code // contract bytecode, which gets set when code is loaded - originStorage Storage // Storage cache of original entries to dedup rewrites - dirtyStorage Storage // Storage entries that need to be flushed to disk - fakeStorage Storage // Fake storage which constructed by caller for debugging purpose. + originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction + pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block + dirtyStorage Storage // Storage entries that have been modified in the current transaction execution + fakeStorage Storage // Fake storage which constructed by caller for debugging purpose. // Cache flags. // When an object is marked suicided it will be delete from the trie @@ -113,13 +114,17 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject { if data.CodeHash == nil { data.CodeHash = emptyCodeHash } + if data.Root == (common.Hash{}) { + data.Root = emptyRoot + } return &stateObject{ - db: db, - address: address, - addrHash: crypto.Keccak256Hash(address[:]), - data: data, - originStorage: make(Storage), - dirtyStorage: make(Storage), + db: db, + address: address, + addrHash: crypto.Keccak256Hash(address[:]), + data: data, + originStorage: make(Storage), + pendingStorage: make(Storage), + dirtyStorage: make(Storage), } } @@ -183,9 +188,11 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has if s.fakeStorage != nil { return s.fakeStorage[key] } - // If we have the original value cached, return that - value, cached := s.originStorage[key] - if cached { + // If we have a pending write or clean cached, return that + if value, pending := s.pendingStorage[key]; pending { + return value + } + if value, cached := s.originStorage[key]; cached { return value } // Track the amount of time wasted on reading the storage trie @@ -198,6 +205,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has s.setError(err) return common.Hash{} } + var value common.Hash if len(enc) > 0 { _, content, _, err := rlp.Split(enc) if err != nil { @@ -252,17 +260,29 @@ func (s *stateObject) setState(key, value common.Hash) { s.dirtyStorage[key] = value } +// finalise moves all dirty storage slots into the pending area to be hashed or +// committed later. It is invoked at the end of every transaction. +func (s *stateObject) finalise() { + for key, value := range s.dirtyStorage { + s.pendingStorage[key] = value + } + if len(s.dirtyStorage) > 0 { + s.dirtyStorage = make(Storage) + } +} + // updateTrie writes cached storage modifications into the object's storage trie. func (s *stateObject) updateTrie(db Database) Trie { + // Make sure all dirty slots are finalized into the pending storage area + s.finalise() + // Track the amount of time wasted on updating the storge trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) } - // Update all the dirty slots in the trie + // Insert all the pending updates into the trie tr := s.getTrie(db) - for key, value := range s.dirtyStorage { - delete(s.dirtyStorage, key) - + for key, value := range s.pendingStorage { // Skip noop changes, persist actual changes if value == s.originStorage[key] { continue @@ -274,9 +294,12 @@ func (s *stateObject) updateTrie(db Database) Trie { continue } // Encoding []byte cannot fail, ok to ignore the error. - v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) + v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) s.setError(tr.TryUpdate(key[:], v)) } + if len(s.pendingStorage) > 0 { + s.pendingStorage = make(Storage) + } return tr } @@ -356,6 +379,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject { stateObject.code = s.code stateObject.dirtyStorage = s.dirtyStorage.Copy() stateObject.originStorage = s.originStorage.Copy() + stateObject.pendingStorage = s.pendingStorage.Copy() stateObject.suicided = s.suicided stateObject.dirtyCode = s.dirtyCode stateObject.deleted = s.deleted diff --git a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go index b07f08fd21..4b4f374c92 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state/statedb.go @@ -67,8 +67,9 @@ type StateDB struct { trie Trie // This map holds 'live' objects, which will get modified while processing a state transition. - stateObjects map[common.Address]*stateObject - stateObjectsDirty map[common.Address]struct{} + stateObjects map[common.Address]*stateObject + stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie + stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution // DB error. // State objects are used by the consensus core and VM which are @@ -111,13 +112,14 @@ func New(root common.Hash, db Database) (*StateDB, error) { return nil, err } return &StateDB{ - db: db, - trie: tr, - stateObjects: make(map[common.Address]*stateObject), - stateObjectsDirty: make(map[common.Address]struct{}), - logs: make(map[common.Hash][]*types.Log), - preimages: make(map[common.Hash][]byte), - journal: newJournal(), + db: db, + trie: tr, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsPending: make(map[common.Address]struct{}), + stateObjectsDirty: make(map[common.Address]struct{}), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), + journal: newJournal(), }, nil } @@ -141,6 +143,7 @@ func (self *StateDB) Reset(root common.Hash) error { } self.trie = tr self.stateObjects = make(map[common.Address]*stateObject) + self.stateObjectsPending = make(map[common.Address]struct{}) self.stateObjectsDirty = make(map[common.Address]struct{}) self.thash = common.Hash{} self.bhash = common.Hash{} @@ -421,15 +424,15 @@ func (self *StateDB) Suicide(addr common.Address) bool { // // updateStateObject writes the given object to the trie. -func (s *StateDB) updateStateObject(stateObject *stateObject) { +func (s *StateDB) updateStateObject(obj *stateObject) { // Track the amount of time wasted on updating the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Encode the account and update the account trie - addr := stateObject.Address() + addr := obj.Address() - data, err := rlp.EncodeToBytes(stateObject) + data, err := rlp.EncodeToBytes(obj) if err != nil { panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) } @@ -437,25 +440,33 @@ func (s *StateDB) updateStateObject(stateObject *stateObject) { } // deleteStateObject removes the given object from the state trie. -func (s *StateDB) deleteStateObject(stateObject *stateObject) { +func (s *StateDB) deleteStateObject(obj *stateObject) { // Track the amount of time wasted on deleting the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Delete the account from the trie - stateObject.deleted = true - - addr := stateObject.Address() + addr := obj.Address() s.setError(s.trie.TryDelete(addr[:])) } -// Retrieve a state object given by the address. Returns nil if not found. -func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { - // Prefer live objects +// getStateObject retrieves a state object given by the address, returning nil if +// the object is not found or was deleted in this execution context. If you need +// to differentiate between non-existent/just-deleted, use getDeletedStateObject. +func (s *StateDB) getStateObject(addr common.Address) *stateObject { + if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { + return obj + } + return nil +} + +// getDeletedStateObject is similar to getStateObject, but instead of returning +// nil for a deleted state object, it returns the actual object with the deleted +// flag set. This is needed by the state journal to revert to the correct self- +// destructed object instead of wiping all knowledge about the state object. +func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { + // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { - if obj.deleted { - return nil - } return obj } // Track the amount of time wasted on loading the object from the database @@ -486,7 +497,7 @@ func (self *StateDB) setStateObject(object *stateObject) { // Retrieve a state object or create a new state object if nil. func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { stateObject := self.getStateObject(addr) - if stateObject == nil || stateObject.deleted { + if stateObject == nil { stateObject, _ = self.createObject(addr) } return stateObject @@ -495,7 +506,8 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { // createObject creates a new state object. If there is an existing account with // the given address, it is overwritten and returned as the second return value. func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { - prev = self.getStateObject(addr) + prev = self.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! + newobj = newObject(self, addr, Account{}) newobj.setNonce(0) // sets the object to dirty if prev == nil { @@ -558,15 +570,16 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common func (self *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ - db: self.db, - trie: self.db.CopyTrie(self.trie), - stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)), - refund: self.refund, - logs: make(map[common.Hash][]*types.Log, len(self.logs)), - logSize: self.logSize, - preimages: make(map[common.Hash][]byte, len(self.preimages)), - journal: newJournal(), + db: self.db, + trie: self.db.CopyTrie(self.trie), + stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)), + stateObjectsPending: make(map[common.Address]struct{}, len(self.stateObjectsPending)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)), + refund: self.refund, + logs: make(map[common.Hash][]*types.Log, len(self.logs)), + logSize: self.logSize, + preimages: make(map[common.Hash][]byte, len(self.preimages)), + journal: newJournal(), } // Copy the dirty states, logs, and preimages for addr := range self.journal.dirties { @@ -575,18 +588,29 @@ func (self *StateDB) Copy() *StateDB { // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for // nil if object, exist := self.stateObjects[addr]; exist { + // Even though the original object is dirty, we are not copying the journal, + // so we need to make sure that anyside effect the journal would have caused + // during a commit (or similar op) is already applied to the copy. state.stateObjects[addr] = object.deepCopy(state) - state.stateObjectsDirty[addr] = struct{}{} + + state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits + state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits } } // Above, we don't copy the actual journal. This means that if the copy is copied, the // loop above will be a no-op, since the copy's journal is empty. // Thus, here we iterate over stateObjects, to enable copies of copies + for addr := range self.stateObjectsPending { + if _, exist := state.stateObjects[addr]; !exist { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) + } + state.stateObjectsPending[addr] = struct{}{} + } for addr := range self.stateObjectsDirty { if _, exist := state.stateObjects[addr]; !exist { state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) - state.stateObjectsDirty[addr] = struct{}{} } + state.stateObjectsDirty[addr] = struct{}{} } for hash, logs := range self.logs { cpy := make([]*types.Log, len(logs)) @@ -631,11 +655,12 @@ func (self *StateDB) GetRefund() uint64 { return self.refund } -// Finalise finalises the state by removing the self destructed objects -// and clears the journal as well as the refunds. +// Finalise finalises the state by removing the self destructed objects and clears +// the journal as well as the refunds. Finalise, however, will not push any updates +// into the tries just yet. Only IntermediateRoot or Commit will do that. func (s *StateDB) Finalise(deleteEmptyObjects bool) { for addr := range s.journal.dirties { - stateObject, exist := s.stateObjects[addr] + obj, exist := s.stateObjects[addr] if !exist { // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 // That tx goes out of gas, and although the notion of 'touched' does not exist there, the @@ -645,13 +670,12 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // Thus, we can safely ignore it here continue } - - if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { - s.deleteStateObject(stateObject) + if obj.suicided || (deleteEmptyObjects && obj.empty()) { + obj.deleted = true } else { - stateObject.updateRoot(s.db) - s.updateStateObject(stateObject) + obj.finalise() } + s.stateObjectsPending[addr] = struct{}{} s.stateObjectsDirty[addr] = struct{}{} } // Invalidate journal because reverting across transactions is not allowed. @@ -662,8 +686,21 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // It is called in between transactions to get the root hash that // goes into transaction receipts. func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { + // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) + for addr := range s.stateObjectsPending { + obj := s.stateObjects[addr] + if obj.deleted { + s.deleteStateObject(obj) + } else { + obj.updateRoot(s.db) + s.updateStateObject(obj) + } + } + if len(s.stateObjectsPending) > 0 { + s.stateObjectsPending = make(map[common.Address]struct{}) + } // Track the amount of time wasted on hashing the account trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) @@ -680,46 +717,40 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) { } func (s *StateDB) clearJournalAndRefund() { - s.journal = newJournal() - s.validRevisions = s.validRevisions[:0] - s.refund = 0 + if len(s.journal.entries) > 0 { + s.journal = newJournal() + s.refund = 0 + } + s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entires } // Commit writes the state to the underlying in-memory trie database. -func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { - defer s.clearJournalAndRefund() +func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { + // Finalize any pending changes and merge everything into the tries + s.IntermediateRoot(deleteEmptyObjects) - for addr := range s.journal.dirties { - s.stateObjectsDirty[addr] = struct{}{} - } // Commit objects to the trie, measuring the elapsed time - for addr, stateObject := range s.stateObjects { - _, isDirty := s.stateObjectsDirty[addr] - switch { - case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): - // If the object has been removed, don't bother syncing it - // and just mark it for deletion in the trie. - s.deleteStateObject(stateObject) - case isDirty: + for addr := range s.stateObjectsDirty { + if obj := s.stateObjects[addr]; !obj.deleted { // Write any contract code associated with the state object - if stateObject.code != nil && stateObject.dirtyCode { - s.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code) - stateObject.dirtyCode = false + if obj.code != nil && obj.dirtyCode { + s.db.TrieDB().InsertBlob(common.BytesToHash(obj.CodeHash()), obj.code) + obj.dirtyCode = false } - // Write any storage changes in the state object to its storage trie. - if err := stateObject.CommitTrie(s.db); err != nil { + // Write any storage changes in the state object to its storage trie + if err := obj.CommitTrie(s.db); err != nil { return common.Hash{}, err } - // Update the object in the main account trie. - s.updateStateObject(stateObject) } - delete(s.stateObjectsDirty, addr) + } + if len(s.stateObjectsDirty) > 0 { + s.stateObjectsDirty = make(map[common.Address]struct{}) } // Write the account trie changes, measuing the amount of wasted time if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now()) } - root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error { + return s.trie.Commit(func(leaf []byte, parent common.Hash) error { var account Account if err := rlp.DecodeBytes(leaf, &account); err != nil { return nil @@ -733,5 +764,4 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) } return nil }) - return root, err } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go index bed6a07306..cfe17d587b 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_processor.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_processor.go @@ -68,7 +68,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Iterate over and process the individual transactions for i, tx := range block.Transactions() { statedb.Prepare(tx.Hash(), block.Hash(), i) - receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) + receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) if err != nil { return nil, nil, 0, err } @@ -85,10 +85,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment. It returns the receipt // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. -func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) { +func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) if err != nil { - return nil, 0, err + return nil, err } // Create a new context to be used in the EVM environment context := NewEVMContext(msg, header, bc, author) @@ -98,7 +98,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // Apply the transaction to the current state (included in the env) _, gas, failed, err := ApplyMessage(vmenv, msg, gp) if err != nil { - return nil, 0, err + return nil, err } // Update the state with pending changes var root []byte @@ -125,5 +125,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo receipt.BlockNumber = header.Number receipt.TransactionIndex = uint(statedb.TxIndex()) - return receipt, gas, err + return receipt, err } diff --git a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go index fda081b7d1..bef6e9b0eb 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/state_transition.go +++ b/vendor/github.com/ethereum/go-ethereum/core/state_transition.go @@ -76,10 +76,10 @@ type Message interface { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) { +func IntrinsicGas(data []byte, contractCreation, isEIP155 bool, isEIP2028 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 - if contractCreation && homestead { + if contractCreation && isEIP155 { gas = params.TxGasContractCreation } else { gas = params.TxGas @@ -94,10 +94,14 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) } } // Make sure we don't exceed uint64 for all data combinations - if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz { + nonZeroGas := params.TxDataNonZeroGasFrontier + if isEIP2028 { + nonZeroGas = params.TxDataNonZeroGasEIP2028 + } + if (math.MaxUint64-gas)/nonZeroGas < nz { return 0, vm.ErrOutOfGas } - gas += nz * params.TxDataNonZeroGas + gas += nz * nonZeroGas z := uint64(len(data)) - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { @@ -187,10 +191,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo msg := st.msg sender := vm.AccountRef(msg.From()) homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) + istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber) contractCreation := msg.To() == nil // Pay intrinsic gas - gas, err := IntrinsicGas(st.data, contractCreation, homestead) + gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul) if err != nil { return nil, 0, false, err } diff --git a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go index 0c422dd99d..f7032dbd1e 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go +++ b/vendor/github.com/ethereum/go-ethereum/core/tx_pool.go @@ -97,13 +97,14 @@ var ( queuedNofundsMeter = metrics.NewRegisteredMeter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds // General tx metrics - validMeter = metrics.NewRegisteredMeter("txpool/valid", nil) + knownTxMeter = metrics.NewRegisteredMeter("txpool/known", nil) + validTxMeter = metrics.NewRegisteredMeter("txpool/valid", nil) invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil) underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil) - pendingCounter = metrics.NewRegisteredCounter("txpool/pending", nil) - queuedCounter = metrics.NewRegisteredCounter("txpool/queued", nil) - localCounter = metrics.NewRegisteredCounter("txpool/local", nil) + pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil) + queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil) + localGauge = metrics.NewRegisteredGauge("txpool/local", nil) ) // TxStatus is the current status of a transaction as seen by the pool. @@ -217,6 +218,8 @@ type TxPool struct { signer types.Signer mu sync.RWMutex + istanbul bool // Fork indicator whether we are in the istanbul stage. + currentState *state.StateDB // Current state in the blockchain head pendingNonces *txNoncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps @@ -540,7 +543,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true) + intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul) if err != nil { return err } @@ -562,16 +565,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e hash := tx.Hash() if pool.all.Get(hash) != nil { log.Trace("Discarding already known transaction", "hash", hash) + knownTxMeter.Mark(1) return false, fmt.Errorf("known transaction: %x", hash) } - // If the transaction fails basic validation, discard it if err := pool.validateTx(tx, local); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err } - // If the transaction pool is full, discard underpriced transactions if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it @@ -588,7 +590,6 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e pool.removeTx(tx.Hash(), false) } } - // Try to replace an existing transaction in the pending pool from, _ := types.Sender(pool.signer, tx) // already validated if list := pool.pending[from]; list != nil && list.Overlaps(tx) { @@ -611,13 +612,11 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To()) return old != nil, nil } - // New transaction isn't replacing a pending one, push into queue replaced, err = pool.enqueueTx(hash, tx) if err != nil { return false, err } - // Mark local addresses and journal local transactions if local { if !pool.locals.contains(from) { @@ -626,7 +625,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e } } if local || pool.locals.contains(from) { - localCounter.Inc(1) + localGauge.Inc(1) } pool.journalTx(from, tx) @@ -656,7 +655,7 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er queuedReplaceMeter.Mark(1) } else { // Nothing was replaced, bump the queued counter - queuedCounter.Inc(1) + queuedGauge.Inc(1) } if pool.all.Get(hash) == nil { pool.all.Add(tx) @@ -705,7 +704,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T pendingReplaceMeter.Mark(1) } else { // Nothing was replaced, bump the pending counter - pendingCounter.Inc(1) + pendingGauge.Inc(1) } // Failsafe to work around direct pending inserts (tests) if pool.all.Get(hash) == nil { @@ -766,15 +765,41 @@ func (pool *TxPool) AddRemote(tx *types.Transaction) error { // addTxs attempts to queue a batch of transactions if they are valid. func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error { + // Filter out known ones without obtaining the pool lock or recovering signatures + var ( + errs = make([]error, len(txs)) + news = make([]*types.Transaction, 0, len(txs)) + ) + for i, tx := range txs { + // If the transaction is known, pre-set the error slot + if pool.all.Get(tx.Hash()) != nil { + errs[i] = fmt.Errorf("known transaction: %x", tx.Hash()) + knownTxMeter.Mark(1) + continue + } + // Accumulate all unknown transactions for deeper processing + news = append(news, tx) + } + if len(news) == 0 { + return errs + } // Cache senders in transactions before obtaining lock (pool.signer is immutable) - for _, tx := range txs { + for _, tx := range news { types.Sender(pool.signer, tx) } - + // Process all the new transaction and merge any errors into the original slice pool.mu.Lock() - errs, dirtyAddrs := pool.addTxsLocked(txs, local) + newErrs, dirtyAddrs := pool.addTxsLocked(news, local) pool.mu.Unlock() + var nilSlot = 0 + for _, err := range newErrs { + for errs[nilSlot] != nil { + nilSlot++ + } + errs[nilSlot] = err + } + // Reorg the pool internals if needed and return done := pool.requestPromoteExecutables(dirtyAddrs) if sync { <-done @@ -794,26 +819,29 @@ func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) ([]error, dirty.addTx(tx) } } - validMeter.Mark(int64(len(dirty.accounts))) + validTxMeter.Mark(int64(len(dirty.accounts))) return errs, dirty } // Status returns the status (unknown/pending/queued) of a batch of transactions // identified by their hashes. func (pool *TxPool) Status(hashes []common.Hash) []TxStatus { - pool.mu.RLock() - defer pool.mu.RUnlock() - status := make([]TxStatus, len(hashes)) for i, hash := range hashes { - if tx := pool.all.Get(hash); tx != nil { - from, _ := types.Sender(pool.signer, tx) // already validated - if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil { - status[i] = TxStatusPending - } else { - status[i] = TxStatusQueued - } + tx := pool.Get(hash) + if tx == nil { + continue + } + from, _ := types.Sender(pool.signer, tx) // already validated + pool.mu.RLock() + if txList := pool.pending[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil { + status[i] = TxStatusPending + } else if txList := pool.queue[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil { + status[i] = TxStatusQueued } + // implicit else: the tx may have been included into a block between + // checking pool.Get and obtaining the lock. In that case, TxStatusUnknown is correct + pool.mu.RUnlock() } return status } @@ -839,7 +867,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { pool.priced.Removed(1) } if pool.locals.contains(addr) { - localCounter.Dec(1) + localGauge.Dec(1) } // Remove the transaction from the pending lists and reset the account nonce if pending := pool.pending[addr]; pending != nil { @@ -856,7 +884,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { // Update the account nonce if needed pool.pendingNonces.setIfLower(addr, tx.Nonce()) // Reduce the pending counter - pendingCounter.Dec(int64(1 + len(invalids))) + pendingGauge.Dec(int64(1 + len(invalids))) return } } @@ -864,7 +892,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { if future := pool.queue[addr]; future != nil { if removed, _ := future.Remove(tx); removed { // Reduce the queued counter - queuedCounter.Dec(1) + queuedGauge.Dec(1) } if future.Empty() { delete(pool.queue, addr) @@ -1118,6 +1146,10 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { log.Debug("Reinjecting stale transactions", "count", len(reinject)) senderCacher.recover(pool.signer, reinject) pool.addTxsLocked(reinject, false) + + // Update all fork indicator by next pending block number. + next := new(big.Int).Add(newHead.Number, big.NewInt(1)) + pool.istanbul = pool.chainconfig.IsIstanbul(next) } // promoteExecutables moves transactions that have become processable from the @@ -1158,7 +1190,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans promoted = append(promoted, tx) } } - queuedCounter.Dec(int64(len(readies))) + queuedGauge.Dec(int64(len(readies))) // Drop all transactions over the allowed limit var caps types.Transactions @@ -1173,9 +1205,9 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans } // Mark all the items dropped as removed pool.priced.Removed(len(forwards) + len(drops) + len(caps)) - queuedCounter.Dec(int64(len(forwards) + len(drops) + len(caps))) + queuedGauge.Dec(int64(len(forwards) + len(drops) + len(caps))) if pool.locals.contains(addr) { - localCounter.Dec(int64(len(forwards) + len(drops) + len(caps))) + localGauge.Dec(int64(len(forwards) + len(drops) + len(caps))) } // Delete the entire queue entry if it became empty. if list.Empty() { @@ -1234,9 +1266,9 @@ func (pool *TxPool) truncatePending() { log.Trace("Removed fairness-exceeding pending transaction", "hash", hash) } pool.priced.Removed(len(caps)) - pendingCounter.Dec(int64(len(caps))) + pendingGauge.Dec(int64(len(caps))) if pool.locals.contains(offenders[i]) { - localCounter.Dec(int64(len(caps))) + localGauge.Dec(int64(len(caps))) } pending-- } @@ -1261,9 +1293,9 @@ func (pool *TxPool) truncatePending() { log.Trace("Removed fairness-exceeding pending transaction", "hash", hash) } pool.priced.Removed(len(caps)) - pendingCounter.Dec(int64(len(caps))) + pendingGauge.Dec(int64(len(caps))) if pool.locals.contains(addr) { - localCounter.Dec(int64(len(caps))) + localGauge.Dec(int64(len(caps))) } pending-- } @@ -1347,9 +1379,9 @@ func (pool *TxPool) demoteUnexecutables() { log.Trace("Demoting pending transaction", "hash", hash) pool.enqueueTx(hash, tx) } - pendingCounter.Dec(int64(len(olds) + len(drops) + len(invalids))) + pendingGauge.Dec(int64(len(olds) + len(drops) + len(invalids))) if pool.locals.contains(addr) { - localCounter.Dec(int64(len(olds) + len(drops) + len(invalids))) + localGauge.Dec(int64(len(olds) + len(drops) + len(invalids))) } // If there's a gap in front, alert (should never happen) and postpone all transactions if list.Len() > 0 && list.txs.Get(nonce) == nil { @@ -1359,7 +1391,7 @@ func (pool *TxPool) demoteUnexecutables() { log.Error("Demoting invalidated transaction", "hash", hash) pool.enqueueTx(hash, tx) } - pendingCounter.Dec(int64(len(gapped))) + pendingGauge.Dec(int64(len(gapped))) } // Delete the entire queue entry if it became empty. if list.Empty() { diff --git a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go index 63132048ee..842fedbd03 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go +++ b/vendor/github.com/ethereum/go-ethereum/core/types/transaction_signing.go @@ -193,8 +193,8 @@ func (s FrontierSigner) Equal(s2 Signer) bool { // SignatureValues returns signature values. This signature // needs to be in the [R || S || V] format where V is 0 or 1. func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { - if len(sig) != 65 { - panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) + if len(sig) != crypto.SignatureLength { + panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength)) } r = new(big.Int).SetBytes(sig[:32]) s = new(big.Int).SetBytes(sig[32:64]) @@ -229,7 +229,7 @@ func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (commo } // encode the signature in uncompressed format r, s := R.Bytes(), S.Bytes() - sig := make([]byte, 65) + sig := make([]byte, crypto.SignatureLength) copy(sig[32-len(r):32], r) copy(sig[64-len(s):64], s) sig[64] = V diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go b/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go index 0e4fe01981..9b0ba09ed1 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/contracts.go @@ -18,12 +18,14 @@ package vm import ( "crypto/sha256" + "encoding/binary" "errors" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/blake2b" "github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/params" "golang.org/x/crypto/ripemd160" @@ -70,6 +72,7 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{}, common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{}, + common.BytesToAddress([]byte{9}): &blake2F{}, } // RunPrecompiledContract runs and evaluates the output of a precompiled contract. @@ -103,8 +106,13 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) { if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { return nil, nil } + // We must make sure not to modify the 'input', so placing the 'v' along with + // the signature needs to be done on a new allocation + sig := make([]byte, 65) + copy(sig, input[64:128]) + sig[64] = v // v needs to be at the end for libsecp256k1 - pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v)) + pubKey, err := crypto.Ecrecover(input[:32], sig) // make sure the public key is a valid one if err != nil { return nil, nil @@ -431,3 +439,64 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 { func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) { return runBn256Pairing(input) } + +type blake2F struct{} + +func (c *blake2F) RequiredGas(input []byte) uint64 { + // If the input is malformed, we can't calculate the gas, return 0 and let the + // actual call choke and fault. + if len(input) != blake2FInputLength { + return 0 + } + return uint64(binary.BigEndian.Uint32(input[0:4])) +} + +const ( + blake2FInputLength = 213 + blake2FFinalBlockBytes = byte(1) + blake2FNonFinalBlockBytes = byte(0) +) + +var ( + errBlake2FInvalidInputLength = errors.New("invalid input length") + errBlake2FInvalidFinalFlag = errors.New("invalid final flag") +) + +func (c *blake2F) Run(input []byte) ([]byte, error) { + // Make sure the input is valid (correct lenth and final flag) + if len(input) != blake2FInputLength { + return nil, errBlake2FInvalidInputLength + } + if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes { + return nil, errBlake2FInvalidFinalFlag + } + // Parse the input into the Blake2b call parameters + var ( + rounds = binary.BigEndian.Uint32(input[0:4]) + final = (input[212] == blake2FFinalBlockBytes) + + h [8]uint64 + m [16]uint64 + t [2]uint64 + ) + for i := 0; i < 8; i++ { + offset := 4 + i*8 + h[i] = binary.LittleEndian.Uint64(input[offset : offset+8]) + } + for i := 0; i < 16; i++ { + offset := 68 + i*8 + m[i] = binary.LittleEndian.Uint64(input[offset : offset+8]) + } + t[0] = binary.LittleEndian.Uint64(input[196:204]) + t[1] = binary.LittleEndian.Uint64(input[204:212]) + + // Execute the compression function, extract and return the result + blake2b.F(&h, m, t, final, rounds) + + output := make([]byte, 64) + for i := 0; i < 8; i++ { + offset := i * 8 + binary.LittleEndian.PutUint64(output[offset:offset+8], h[i]) + } + return output, nil +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/eips.go b/vendor/github.com/ethereum/go-ethereum/core/vm/eips.go index 6e7259d405..075f5b7606 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/eips.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/eips.go @@ -27,6 +27,8 @@ import ( // defined jump tables are not polluted. func EnableEIP(eipNum int, jt *JumpTable) error { switch eipNum { + case 2200: + enable2200(jt) case 1884: enable1884(jt) case 1344: @@ -83,3 +85,8 @@ func opChainID(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo stack.push(chainId) return nil, nil } + +// enable2200 applies EIP-2200 (Rebalance net-metered SSTORE) +func enable2200(jt *JumpTable) { + jt[SSTORE].dynamicGas = gasSStoreEIP2200 +} diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go index b2999fdea7..1d3c4f1003 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/gas_table.go @@ -17,6 +17,8 @@ package vm import ( + "errors" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" @@ -160,6 +162,61 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi return params.NetSstoreDirtyGas, nil } +// 0. If *gasleft* is less than or equal to 2300, fail the current call. +// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted. +// 2. If current value does not equal new value: +// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context): +// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted. +// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter. +// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses: +// 2.2.1. If original value is not 0: +// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0. +// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter. +// 2.2.2. If original value equals new value (this storage slot is reset): +// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter. +// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter. +func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + // If we fail the minimum gas availability invariant, fail (0) + if contract.Gas <= params.SstoreSentryGasEIP2200 { + return 0, errors.New("not enough gas for reentrancy sentry") + } + // Gas sentry honoured, do the actual gas calculation based on the stored value + var ( + y, x = stack.Back(1), stack.Back(0) + current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + ) + value := common.BigToHash(y) + + if current == value { // noop (1) + return params.SstoreNoopGasEIP2200, nil + } + original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x)) + if original == current { + if original == (common.Hash{}) { // create slot (2.1.1) + return params.SstoreInitGasEIP2200, nil + } + if value == (common.Hash{}) { // delete slot (2.1.2b) + evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + } + return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2) + } + if original != (common.Hash{}) { + if current == (common.Hash{}) { // recreate slot (2.2.1.1) + evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200) + } else if value == (common.Hash{}) { // delete slot (2.2.1.2) + evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200) + } + } + if original == value { + if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1) + evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200) + } else { // reset to original existing slot (2.2.2.2) + evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200) + } + } + return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2) +} + func makeGasLog(n uint64) gasFunc { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { requestedSize, overflow := bigUint64(stack.Back(1)) diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go index 7b6909c927..d65664b67d 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/instructions.go @@ -384,7 +384,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory * func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() - data := memory.Get(offset.Int64(), size.Int64()) + data := memory.GetPtr(offset.Int64(), size.Int64()) if interpreter.hasher == nil { interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) @@ -602,11 +602,9 @@ func opPop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory * } func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - offset := stack.pop() - val := interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32)) - stack.push(val) - - interpreter.intPool.put(offset) + v := stack.peek() + offset := v.Int64() + v.SetBytes(memory.GetPtr(offset, 32)) return nil, nil } @@ -691,7 +689,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor var ( value = stack.pop() offset, size = stack.pop(), stack.pop() - input = memory.Get(offset.Int64(), size.Int64()) + input = memory.GetCopy(offset.Int64(), size.Int64()) gas = contract.Gas ) if interpreter.evm.chainRules.IsEIP150 { @@ -725,7 +723,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo endowment = stack.pop() offset, size = stack.pop(), stack.pop() salt = stack.pop() - input = memory.Get(offset.Int64(), size.Int64()) + input = memory.GetCopy(offset.Int64(), size.Int64()) gas = contract.Gas ) @@ -757,7 +755,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory toAddr := common.BigToAddress(addr) value = math.U256(value) // Get the arguments from the memory. - args := memory.Get(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) if value.Sign() != 0 { gas += params.CallStipend @@ -786,7 +784,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, mem toAddr := common.BigToAddress(addr) value = math.U256(value) // Get arguments from the memory. - args := memory.Get(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) if value.Sign() != 0 { gas += params.CallStipend @@ -814,7 +812,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(addr) // Get arguments from the memory. - args := memory.Get(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas) if err != nil { @@ -839,7 +837,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, m addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.BigToAddress(addr) // Get arguments from the memory. - args := memory.Get(inOffset.Int64(), inSize.Int64()) + args := memory.GetPtr(inOffset.Int64(), inSize.Int64()) ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas) if err != nil { @@ -895,7 +893,7 @@ func makeLog(size int) executionFunc { topics[i] = common.BigToHash(stack.pop()) } - d := memory.Get(mStart.Int64(), mSize.Int64()) + d := memory.GetCopy(mStart.Int64(), mSize.Int64()) interpreter.evm.StateDB.AddLog(&types.Log{ Address: contract.Address(), Topics: topics, diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go b/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go index be6e00a6e6..fe06492de3 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/interpreter.go @@ -93,6 +93,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { if !cfg.JumpTable[STOP].valid { var jt JumpTable switch { + case evm.chainRules.IsIstanbul: + jt = istanbulInstructionSet case evm.chainRules.IsConstantinople: jt = constantinopleInstructionSet case evm.chainRules.IsByzantium: diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go index da532541c6..b26b55284c 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/jump_table.go @@ -60,15 +60,27 @@ var ( spuriousDragonInstructionSet = newSpuriousDragonInstructionSet() byzantiumInstructionSet = newByzantiumInstructionSet() constantinopleInstructionSet = newConstantinopleInstructionSet() + istanbulInstructionSet = newIstanbulInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. type JumpTable [256]operation -// NewConstantinopleInstructionSet returns the frontier, homestead +// newIstanbulInstructionSet returns the frontier, homestead +// byzantium, contantinople and petersburg instructions. +func newIstanbulInstructionSet() JumpTable { + instructionSet := newConstantinopleInstructionSet() + + enable1344(&instructionSet) // ChainID opcode - https://eips.ethereum.org/EIPS/eip-1344 + enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884 + enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200 + + return instructionSet +} + +// newConstantinopleInstructionSet returns the frontier, homestead // byzantium and contantinople instructions. func newConstantinopleInstructionSet() JumpTable { - // instructions that can be executed during the byzantium phase. instructionSet := newByzantiumInstructionSet() instructionSet[SHL] = operation{ execute: opSHL, @@ -112,10 +124,9 @@ func newConstantinopleInstructionSet() JumpTable { return instructionSet } -// NewByzantiumInstructionSet returns the frontier, homestead and +// newByzantiumInstructionSet returns the frontier, homestead and // byzantium instructions. func newByzantiumInstructionSet() JumpTable { - // instructions that can be executed during the homestead phase. instructionSet := newSpuriousDragonInstructionSet() instructionSet[STATICCALL] = operation{ execute: opStaticCall, @@ -177,7 +188,7 @@ func newTangerineWhistleInstructionSet() JumpTable { return instructionSet } -// NewHomesteadInstructionSet returns the frontier and homestead +// newHomesteadInstructionSet returns the frontier and homestead // instructions that can be executed during the homestead phase. func newHomesteadInstructionSet() JumpTable { instructionSet := newFrontierInstructionSet() @@ -194,7 +205,7 @@ func newHomesteadInstructionSet() JumpTable { return instructionSet } -// NewFrontierInstructionSet returns the frontier instructions +// newFrontierInstructionSet returns the frontier instructions // that can be executed during the frontier phase. func newFrontierInstructionSet() JumpTable { return JumpTable{ diff --git a/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go b/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go index 7e6f0eb940..496a4024ba 100644 --- a/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go +++ b/vendor/github.com/ethereum/go-ethereum/core/vm/memory.go @@ -70,7 +70,7 @@ func (m *Memory) Resize(size uint64) { } // Get returns offset + size as a new slice -func (m *Memory) Get(offset, size int64) (cpy []byte) { +func (m *Memory) GetCopy(offset, size int64) (cpy []byte) { if size == 0 { return nil } diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b.go new file mode 100644 index 0000000000..5da50cab6f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b.go @@ -0,0 +1,319 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693 +// and the extendable output function (XOF) BLAKE2Xb. +// +// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf +// and for BLAKE2Xb see https://blake2.net/blake2x.pdf +// +// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512). +// If you need a secret-key MAC (message authentication code), use the New512 +// function with a non-nil key. +// +// BLAKE2X is a construction to compute hash values larger than 64 bytes. It +// can produce hash values between 0 and 4 GiB. +package blake2b + +import ( + "encoding/binary" + "errors" + "hash" +) + +const ( + // The blocksize of BLAKE2b in bytes. + BlockSize = 128 + // The hash size of BLAKE2b-512 in bytes. + Size = 64 + // The hash size of BLAKE2b-384 in bytes. + Size384 = 48 + // The hash size of BLAKE2b-256 in bytes. + Size256 = 32 +) + +var ( + useAVX2 bool + useAVX bool + useSSE4 bool +) + +var ( + errKeySize = errors.New("blake2b: invalid key size") + errHashSize = errors.New("blake2b: invalid hash size") +) + +var iv = [8]uint64{ + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, +} + +// Sum512 returns the BLAKE2b-512 checksum of the data. +func Sum512(data []byte) [Size]byte { + var sum [Size]byte + checkSum(&sum, Size, data) + return sum +} + +// Sum384 returns the BLAKE2b-384 checksum of the data. +func Sum384(data []byte) [Size384]byte { + var sum [Size]byte + var sum384 [Size384]byte + checkSum(&sum, Size384, data) + copy(sum384[:], sum[:Size384]) + return sum384 +} + +// Sum256 returns the BLAKE2b-256 checksum of the data. +func Sum256(data []byte) [Size256]byte { + var sum [Size]byte + var sum256 [Size256]byte + checkSum(&sum, Size256, data) + copy(sum256[:], sum[:Size256]) + return sum256 +} + +// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil +// key turns the hash into a MAC. The key must be between zero and 64 bytes long. +func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) } + +// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil +// key turns the hash into a MAC. The key must be between zero and 64 bytes long. +func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) } + +// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil +// key turns the hash into a MAC. The key must be between zero and 64 bytes long. +func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) } + +// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length. +// A non-nil key turns the hash into a MAC. The key must be between zero and 64 bytes long. +// The hash size can be a value between 1 and 64 but it is highly recommended to use +// values equal or greater than: +// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long). +// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long). +// When the key is nil, the returned hash.Hash implements BinaryMarshaler +// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash. +func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) } + +// F is a compression function for BLAKE2b. It takes as an argument the state +// vector `h`, message block vector `m`, offset counter `t`, final block indicator +// flag `f`, and number of rounds `rounds`. The state vector provided as the first +// parameter is modified by the function. +func F(h *[8]uint64, m [16]uint64, c [2]uint64, final bool, rounds uint32) { + var flag uint64 + if final { + flag = 0xFFFFFFFFFFFFFFFF + } + f(h, &m, c[0], c[1], flag, uint64(rounds)) +} + +func newDigest(hashSize int, key []byte) (*digest, error) { + if hashSize < 1 || hashSize > Size { + return nil, errHashSize + } + if len(key) > Size { + return nil, errKeySize + } + d := &digest{ + size: hashSize, + keyLen: len(key), + } + copy(d.key[:], key) + d.Reset() + return d, nil +} + +func checkSum(sum *[Size]byte, hashSize int, data []byte) { + h := iv + h[0] ^= uint64(hashSize) | (1 << 16) | (1 << 24) + var c [2]uint64 + + if length := len(data); length > BlockSize { + n := length &^ (BlockSize - 1) + if length == n { + n -= BlockSize + } + hashBlocks(&h, &c, 0, data[:n]) + data = data[n:] + } + + var block [BlockSize]byte + offset := copy(block[:], data) + remaining := uint64(BlockSize - offset) + if c[0] < remaining { + c[1]-- + } + c[0] -= remaining + + hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:]) + + for i, v := range h[:(hashSize+7)/8] { + binary.LittleEndian.PutUint64(sum[8*i:], v) + } +} + +func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { + var m [16]uint64 + c0, c1 := c[0], c[1] + + for i := 0; i < len(blocks); { + c0 += BlockSize + if c0 < BlockSize { + c1++ + } + for j := range m { + m[j] = binary.LittleEndian.Uint64(blocks[i:]) + i += 8 + } + f(h, &m, c0, c1, flag, 12) + } + c[0], c[1] = c0, c1 +} + +type digest struct { + h [8]uint64 + c [2]uint64 + size int + block [BlockSize]byte + offset int + + key [BlockSize]byte + keyLen int +} + +const ( + magic = "b2b" + marshaledSize = len(magic) + 8*8 + 2*8 + 1 + BlockSize + 1 +) + +func (d *digest) MarshalBinary() ([]byte, error) { + if d.keyLen != 0 { + return nil, errors.New("crypto/blake2b: cannot marshal MACs") + } + b := make([]byte, 0, marshaledSize) + b = append(b, magic...) + for i := 0; i < 8; i++ { + b = appendUint64(b, d.h[i]) + } + b = appendUint64(b, d.c[0]) + b = appendUint64(b, d.c[1]) + // Maximum value for size is 64 + b = append(b, byte(d.size)) + b = append(b, d.block[:]...) + b = append(b, byte(d.offset)) + return b, nil +} + +func (d *digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic) || string(b[:len(magic)]) != magic { + return errors.New("crypto/blake2b: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("crypto/blake2b: invalid hash state size") + } + b = b[len(magic):] + for i := 0; i < 8; i++ { + b, d.h[i] = consumeUint64(b) + } + b, d.c[0] = consumeUint64(b) + b, d.c[1] = consumeUint64(b) + d.size = int(b[0]) + b = b[1:] + copy(d.block[:], b[:BlockSize]) + b = b[BlockSize:] + d.offset = int(b[0]) + return nil +} + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Size() int { return d.size } + +func (d *digest) Reset() { + d.h = iv + d.h[0] ^= uint64(d.size) | (uint64(d.keyLen) << 8) | (1 << 16) | (1 << 24) + d.offset, d.c[0], d.c[1] = 0, 0, 0 + if d.keyLen > 0 { + d.block = d.key + d.offset = BlockSize + } +} + +func (d *digest) Write(p []byte) (n int, err error) { + n = len(p) + + if d.offset > 0 { + remaining := BlockSize - d.offset + if n <= remaining { + d.offset += copy(d.block[d.offset:], p) + return + } + copy(d.block[d.offset:], p[:remaining]) + hashBlocks(&d.h, &d.c, 0, d.block[:]) + d.offset = 0 + p = p[remaining:] + } + + if length := len(p); length > BlockSize { + nn := length &^ (BlockSize - 1) + if length == nn { + nn -= BlockSize + } + hashBlocks(&d.h, &d.c, 0, p[:nn]) + p = p[nn:] + } + + if len(p) > 0 { + d.offset += copy(d.block[:], p) + } + + return +} + +func (d *digest) Sum(sum []byte) []byte { + var hash [Size]byte + d.finalize(&hash) + return append(sum, hash[:d.size]...) +} + +func (d *digest) finalize(hash *[Size]byte) { + var block [BlockSize]byte + copy(block[:], d.block[:d.offset]) + remaining := uint64(BlockSize - d.offset) + + c := d.c + if c[0] < remaining { + c[1]-- + } + c[0] -= remaining + + h := d.h + hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:]) + + for i, v := range h { + binary.LittleEndian.PutUint64(hash[8*i:], v) + } +} + +func appendUint64(b []byte, x uint64) []byte { + var a [8]byte + binary.BigEndian.PutUint64(a[:], x) + return append(b, a[:]...) +} + +func appendUint32(b []byte, x uint32) []byte { + var a [4]byte + binary.BigEndian.PutUint32(a[:], x) + return append(b, a[:]...) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + x := binary.BigEndian.Uint64(b) + return b[8:], x +} + +func consumeUint32(b []byte) ([]byte, uint32) { + x := binary.BigEndian.Uint32(b) + return b[4:], x +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2bAVX2_amd64.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2bAVX2_amd64.go new file mode 100644 index 0000000000..0d52b18699 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2bAVX2_amd64.go @@ -0,0 +1,37 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7,amd64,!gccgo,!appengine + +package blake2b + +import "golang.org/x/sys/cpu" + +func init() { + useAVX2 = cpu.X86.HasAVX2 + useAVX = cpu.X86.HasAVX + useSSE4 = cpu.X86.HasSSE41 +} + +//go:noescape +func fAVX2(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) + +//go:noescape +func fAVX(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) + +//go:noescape +func fSSE4(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) + +func f(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) { + switch { + case useAVX2: + fAVX2(h, m, c0, c1, flag, rounds) + case useAVX: + fAVX(h, m, c0, c1, flag, rounds) + case useSSE4: + fSSE4(h, m, c0, c1, flag, rounds) + default: + fGeneric(h, m, c0, c1, flag, rounds) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2bAVX2_amd64.s b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2bAVX2_amd64.s new file mode 100644 index 0000000000..4998af37dd --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2bAVX2_amd64.s @@ -0,0 +1,717 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7,amd64,!gccgo,!appengine + +#include "textflag.h" + +DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 +DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b +DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b +DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1 +DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f +DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b +DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179 +GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403 +DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b +DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403 +DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302 +DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a +DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302 +DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32 + +DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 +DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b +GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b +DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1 +DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f +GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b +DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179 +GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403 +DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16 + +DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302 +DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16 + +#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39 +#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93 +#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e +#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93 +#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39 + +#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \ + VPADDQ m0, Y0, Y0; \ + VPADDQ Y1, Y0, Y0; \ + VPXOR Y0, Y3, Y3; \ + VPSHUFD $-79, Y3, Y3; \ + VPADDQ Y3, Y2, Y2; \ + VPXOR Y2, Y1, Y1; \ + VPSHUFB c40, Y1, Y1; \ + VPADDQ m1, Y0, Y0; \ + VPADDQ Y1, Y0, Y0; \ + VPXOR Y0, Y3, Y3; \ + VPSHUFB c48, Y3, Y3; \ + VPADDQ Y3, Y2, Y2; \ + VPXOR Y2, Y1, Y1; \ + VPADDQ Y1, Y1, t; \ + VPSRLQ $63, Y1, Y1; \ + VPXOR t, Y1, Y1; \ + VPERMQ_0x39_Y1_Y1; \ + VPERMQ_0x4E_Y2_Y2; \ + VPERMQ_0x93_Y3_Y3; \ + VPADDQ m2, Y0, Y0; \ + VPADDQ Y1, Y0, Y0; \ + VPXOR Y0, Y3, Y3; \ + VPSHUFD $-79, Y3, Y3; \ + VPADDQ Y3, Y2, Y2; \ + VPXOR Y2, Y1, Y1; \ + VPSHUFB c40, Y1, Y1; \ + VPADDQ m3, Y0, Y0; \ + VPADDQ Y1, Y0, Y0; \ + VPXOR Y0, Y3, Y3; \ + VPSHUFB c48, Y3, Y3; \ + VPADDQ Y3, Y2, Y2; \ + VPXOR Y2, Y1, Y1; \ + VPADDQ Y1, Y1, t; \ + VPSRLQ $63, Y1, Y1; \ + VPXOR t, Y1, Y1; \ + VPERMQ_0x39_Y3_Y3; \ + VPERMQ_0x4E_Y2_Y2; \ + VPERMQ_0x93_Y1_Y1 + +#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E +#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26 +#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E +#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36 +#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E + +#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n +#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n +#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n +#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n +#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n + +#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01 +#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01 +#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01 +#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01 +#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01 + +#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01 +#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01 +#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01 +#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01 +#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01 + +#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8 +#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01 + +// load msg: Y12 = (i0, i1, i2, i3) +// i0, i1, i2, i3 must not be 0 +#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \ + VMOVQ_SI_X12(i0*8); \ + VMOVQ_SI_X11(i2*8); \ + VPINSRQ_1_SI_X12(i1*8); \ + VPINSRQ_1_SI_X11(i3*8); \ + VINSERTI128 $1, X11, Y12, Y12 + +// load msg: Y13 = (i0, i1, i2, i3) +// i0, i1, i2, i3 must not be 0 +#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \ + VMOVQ_SI_X13(i0*8); \ + VMOVQ_SI_X11(i2*8); \ + VPINSRQ_1_SI_X13(i1*8); \ + VPINSRQ_1_SI_X11(i3*8); \ + VINSERTI128 $1, X11, Y13, Y13 + +// load msg: Y14 = (i0, i1, i2, i3) +// i0, i1, i2, i3 must not be 0 +#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \ + VMOVQ_SI_X14(i0*8); \ + VMOVQ_SI_X11(i2*8); \ + VPINSRQ_1_SI_X14(i1*8); \ + VPINSRQ_1_SI_X11(i3*8); \ + VINSERTI128 $1, X11, Y14, Y14 + +// load msg: Y15 = (i0, i1, i2, i3) +// i0, i1, i2, i3 must not be 0 +#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \ + VMOVQ_SI_X15(i0*8); \ + VMOVQ_SI_X11(i2*8); \ + VPINSRQ_1_SI_X15(i1*8); \ + VPINSRQ_1_SI_X11(i3*8); \ + VINSERTI128 $1, X11, Y15, Y15 + +#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \ + VMOVQ_SI_X12_0; \ + VMOVQ_SI_X11(4*8); \ + VPINSRQ_1_SI_X12(2*8); \ + VPINSRQ_1_SI_X11(6*8); \ + VINSERTI128 $1, X11, Y12, Y12; \ + LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \ + LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \ + LOAD_MSG_AVX2_Y15(9, 11, 13, 15) + +#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \ + LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \ + LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \ + VMOVQ_SI_X11(11*8); \ + VPSHUFD $0x4E, 0*8(SI), X14; \ + VPINSRQ_1_SI_X11(5*8); \ + VINSERTI128 $1, X11, Y14, Y14; \ + LOAD_MSG_AVX2_Y15(12, 2, 7, 3) + +#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \ + VMOVQ_SI_X11(5*8); \ + VMOVDQU 11*8(SI), X12; \ + VPINSRQ_1_SI_X11(15*8); \ + VINSERTI128 $1, X11, Y12, Y12; \ + VMOVQ_SI_X13(8*8); \ + VMOVQ_SI_X11(2*8); \ + VPINSRQ_1_SI_X13_0; \ + VPINSRQ_1_SI_X11(13*8); \ + VINSERTI128 $1, X11, Y13, Y13; \ + LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \ + LOAD_MSG_AVX2_Y15(14, 6, 1, 4) + +#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \ + LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \ + LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \ + LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \ + VMOVQ_SI_X15(6*8); \ + VMOVQ_SI_X11_0; \ + VPINSRQ_1_SI_X15(10*8); \ + VPINSRQ_1_SI_X11(8*8); \ + VINSERTI128 $1, X11, Y15, Y15 + +#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \ + LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \ + VMOVQ_SI_X13_0; \ + VMOVQ_SI_X11(4*8); \ + VPINSRQ_1_SI_X13(7*8); \ + VPINSRQ_1_SI_X11(15*8); \ + VINSERTI128 $1, X11, Y13, Y13; \ + LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \ + LOAD_MSG_AVX2_Y15(1, 12, 8, 13) + +#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \ + VMOVQ_SI_X12(2*8); \ + VMOVQ_SI_X11_0; \ + VPINSRQ_1_SI_X12(6*8); \ + VPINSRQ_1_SI_X11(8*8); \ + VINSERTI128 $1, X11, Y12, Y12; \ + LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \ + LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \ + LOAD_MSG_AVX2_Y15(13, 5, 14, 9) + +#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \ + LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \ + LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \ + VMOVQ_SI_X14_0; \ + VPSHUFD $0x4E, 8*8(SI), X11; \ + VPINSRQ_1_SI_X14(6*8); \ + VINSERTI128 $1, X11, Y14, Y14; \ + LOAD_MSG_AVX2_Y15(7, 3, 2, 11) + +#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \ + LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \ + LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \ + LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \ + VMOVQ_SI_X15_0; \ + VMOVQ_SI_X11(6*8); \ + VPINSRQ_1_SI_X15(4*8); \ + VPINSRQ_1_SI_X11(10*8); \ + VINSERTI128 $1, X11, Y15, Y15 + +#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \ + VMOVQ_SI_X12(6*8); \ + VMOVQ_SI_X11(11*8); \ + VPINSRQ_1_SI_X12(14*8); \ + VPINSRQ_1_SI_X11_0; \ + VINSERTI128 $1, X11, Y12, Y12; \ + LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \ + VMOVQ_SI_X11(1*8); \ + VMOVDQU 12*8(SI), X14; \ + VPINSRQ_1_SI_X11(10*8); \ + VINSERTI128 $1, X11, Y14, Y14; \ + VMOVQ_SI_X15(2*8); \ + VMOVDQU 4*8(SI), X11; \ + VPINSRQ_1_SI_X15(7*8); \ + VINSERTI128 $1, X11, Y15, Y15 + +#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \ + LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \ + VMOVQ_SI_X13(2*8); \ + VPSHUFD $0x4E, 5*8(SI), X11; \ + VPINSRQ_1_SI_X13(4*8); \ + VINSERTI128 $1, X11, Y13, Y13; \ + LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \ + VMOVQ_SI_X15(11*8); \ + VMOVQ_SI_X11(12*8); \ + VPINSRQ_1_SI_X15(14*8); \ + VPINSRQ_1_SI_X11_0; \ + VINSERTI128 $1, X11, Y15, Y15 + +// func fAVX2(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) +TEXT ·fAVX2(SB), 4, $64-48 // frame size = 32 + 32 byte alignment + MOVQ h+0(FP), AX + MOVQ m+8(FP), SI + MOVQ c0+16(FP), R8 + MOVQ c1+24(FP), R9 + MOVQ flag+32(FP), CX + MOVQ rounds+40(FP), BX + + MOVQ SP, DX + MOVQ SP, R10 + ADDQ $31, R10 + ANDQ $~31, R10 + MOVQ R10, SP + + MOVQ CX, 16(SP) + XORQ CX, CX + MOVQ CX, 24(SP) + + VMOVDQU ·AVX2_c40<>(SB), Y4 + VMOVDQU ·AVX2_c48<>(SB), Y5 + + VMOVDQU 0(AX), Y8 + VMOVDQU 32(AX), Y9 + VMOVDQU ·AVX2_iv0<>(SB), Y6 + VMOVDQU ·AVX2_iv1<>(SB), Y7 + + MOVQ R8, 0(SP) + MOVQ R9, 8(SP) + + VMOVDQA Y8, Y0 + VMOVDQA Y9, Y1 + VMOVDQA Y6, Y2 + VPXOR 0(SP), Y7, Y3 + +loop: + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() + ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5) + + JMP loop + +done: + VPXOR Y0, Y8, Y8 + VPXOR Y1, Y9, Y9 + VPXOR Y2, Y8, Y8 + VPXOR Y3, Y9, Y9 + + VMOVDQU Y8, 0(AX) + VMOVDQU Y9, 32(AX) + VZEROUPPER + + MOVQ DX, SP + RET + +#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA +#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB +#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF +#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD +#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE + +#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7 +#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF +#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7 +#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF +#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7 +#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7 +#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF +#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF + +#define SHUFFLE_AVX() \ + VMOVDQA X6, X13; \ + VMOVDQA X2, X14; \ + VMOVDQA X4, X6; \ + VPUNPCKLQDQ_X13_X13_X15; \ + VMOVDQA X5, X4; \ + VMOVDQA X6, X5; \ + VPUNPCKHQDQ_X15_X7_X6; \ + VPUNPCKLQDQ_X7_X7_X15; \ + VPUNPCKHQDQ_X15_X13_X7; \ + VPUNPCKLQDQ_X3_X3_X15; \ + VPUNPCKHQDQ_X15_X2_X2; \ + VPUNPCKLQDQ_X14_X14_X15; \ + VPUNPCKHQDQ_X15_X3_X3; \ + +#define SHUFFLE_AVX_INV() \ + VMOVDQA X2, X13; \ + VMOVDQA X4, X14; \ + VPUNPCKLQDQ_X2_X2_X15; \ + VMOVDQA X5, X4; \ + VPUNPCKHQDQ_X15_X3_X2; \ + VMOVDQA X14, X5; \ + VPUNPCKLQDQ_X3_X3_X15; \ + VMOVDQA X6, X14; \ + VPUNPCKHQDQ_X15_X13_X3; \ + VPUNPCKLQDQ_X7_X7_X15; \ + VPUNPCKHQDQ_X15_X6_X6; \ + VPUNPCKLQDQ_X14_X14_X15; \ + VPUNPCKHQDQ_X15_X7_X7; \ + +#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ + VPADDQ m0, v0, v0; \ + VPADDQ v2, v0, v0; \ + VPADDQ m1, v1, v1; \ + VPADDQ v3, v1, v1; \ + VPXOR v0, v6, v6; \ + VPXOR v1, v7, v7; \ + VPSHUFD $-79, v6, v6; \ + VPSHUFD $-79, v7, v7; \ + VPADDQ v6, v4, v4; \ + VPADDQ v7, v5, v5; \ + VPXOR v4, v2, v2; \ + VPXOR v5, v3, v3; \ + VPSHUFB c40, v2, v2; \ + VPSHUFB c40, v3, v3; \ + VPADDQ m2, v0, v0; \ + VPADDQ v2, v0, v0; \ + VPADDQ m3, v1, v1; \ + VPADDQ v3, v1, v1; \ + VPXOR v0, v6, v6; \ + VPXOR v1, v7, v7; \ + VPSHUFB c48, v6, v6; \ + VPSHUFB c48, v7, v7; \ + VPADDQ v6, v4, v4; \ + VPADDQ v7, v5, v5; \ + VPXOR v4, v2, v2; \ + VPXOR v5, v3, v3; \ + VPADDQ v2, v2, t0; \ + VPSRLQ $63, v2, v2; \ + VPXOR t0, v2, v2; \ + VPADDQ v3, v3, t0; \ + VPSRLQ $63, v3, v3; \ + VPXOR t0, v3, v3 + +// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7) +// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0 +#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \ + VMOVQ_SI_X12(i0*8); \ + VMOVQ_SI_X13(i2*8); \ + VMOVQ_SI_X14(i4*8); \ + VMOVQ_SI_X15(i6*8); \ + VPINSRQ_1_SI_X12(i1*8); \ + VPINSRQ_1_SI_X13(i3*8); \ + VPINSRQ_1_SI_X14(i5*8); \ + VPINSRQ_1_SI_X15(i7*8) + +// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7) +#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \ + VMOVQ_SI_X12_0; \ + VMOVQ_SI_X13(4*8); \ + VMOVQ_SI_X14(1*8); \ + VMOVQ_SI_X15(5*8); \ + VPINSRQ_1_SI_X12(2*8); \ + VPINSRQ_1_SI_X13(6*8); \ + VPINSRQ_1_SI_X14(3*8); \ + VPINSRQ_1_SI_X15(7*8) + +// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3) +#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \ + VPSHUFD $0x4E, 0*8(SI), X12; \ + VMOVQ_SI_X13(11*8); \ + VMOVQ_SI_X14(12*8); \ + VMOVQ_SI_X15(7*8); \ + VPINSRQ_1_SI_X13(5*8); \ + VPINSRQ_1_SI_X14(2*8); \ + VPINSRQ_1_SI_X15(3*8) + +// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13) +#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \ + VMOVDQU 11*8(SI), X12; \ + VMOVQ_SI_X13(5*8); \ + VMOVQ_SI_X14(8*8); \ + VMOVQ_SI_X15(2*8); \ + VPINSRQ_1_SI_X13(15*8); \ + VPINSRQ_1_SI_X14_0; \ + VPINSRQ_1_SI_X15(13*8) + +// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8) +#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \ + VMOVQ_SI_X12(2*8); \ + VMOVQ_SI_X13(4*8); \ + VMOVQ_SI_X14(6*8); \ + VMOVQ_SI_X15_0; \ + VPINSRQ_1_SI_X12(5*8); \ + VPINSRQ_1_SI_X13(15*8); \ + VPINSRQ_1_SI_X14(10*8); \ + VPINSRQ_1_SI_X15(8*8) + +// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15) +#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \ + VMOVQ_SI_X12(9*8); \ + VMOVQ_SI_X13(2*8); \ + VMOVQ_SI_X14_0; \ + VMOVQ_SI_X15(4*8); \ + VPINSRQ_1_SI_X12(5*8); \ + VPINSRQ_1_SI_X13(10*8); \ + VPINSRQ_1_SI_X14(7*8); \ + VPINSRQ_1_SI_X15(15*8) + +// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3) +#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \ + VMOVQ_SI_X12(2*8); \ + VMOVQ_SI_X13_0; \ + VMOVQ_SI_X14(12*8); \ + VMOVQ_SI_X15(11*8); \ + VPINSRQ_1_SI_X12(6*8); \ + VPINSRQ_1_SI_X13(8*8); \ + VPINSRQ_1_SI_X14(10*8); \ + VPINSRQ_1_SI_X15(3*8) + +// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11) +#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \ + MOVQ 0*8(SI), X12; \ + VPSHUFD $0x4E, 8*8(SI), X13; \ + MOVQ 7*8(SI), X14; \ + MOVQ 2*8(SI), X15; \ + VPINSRQ_1_SI_X12(6*8); \ + VPINSRQ_1_SI_X14(3*8); \ + VPINSRQ_1_SI_X15(11*8) + +// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8) +#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \ + MOVQ 6*8(SI), X12; \ + MOVQ 11*8(SI), X13; \ + MOVQ 15*8(SI), X14; \ + MOVQ 3*8(SI), X15; \ + VPINSRQ_1_SI_X12(14*8); \ + VPINSRQ_1_SI_X13_0; \ + VPINSRQ_1_SI_X14(9*8); \ + VPINSRQ_1_SI_X15(8*8) + +// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10) +#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \ + MOVQ 5*8(SI), X12; \ + MOVQ 8*8(SI), X13; \ + MOVQ 0*8(SI), X14; \ + MOVQ 6*8(SI), X15; \ + VPINSRQ_1_SI_X12(15*8); \ + VPINSRQ_1_SI_X13(2*8); \ + VPINSRQ_1_SI_X14(4*8); \ + VPINSRQ_1_SI_X15(10*8) + +// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5) +#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \ + VMOVDQU 12*8(SI), X12; \ + MOVQ 1*8(SI), X13; \ + MOVQ 2*8(SI), X14; \ + VPINSRQ_1_SI_X13(10*8); \ + VPINSRQ_1_SI_X14(7*8); \ + VMOVDQU 4*8(SI), X15 + +// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0) +#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \ + MOVQ 15*8(SI), X12; \ + MOVQ 3*8(SI), X13; \ + MOVQ 11*8(SI), X14; \ + MOVQ 12*8(SI), X15; \ + VPINSRQ_1_SI_X12(9*8); \ + VPINSRQ_1_SI_X13(13*8); \ + VPINSRQ_1_SI_X14(14*8); \ + VPINSRQ_1_SI_X15_0 + +// func fAVX(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) +TEXT ·fAVX(SB), 4, $24-48 // frame size = 8 + 16 byte alignment + MOVQ h+0(FP), AX + MOVQ m+8(FP), SI + MOVQ c0+16(FP), R8 + MOVQ c1+24(FP), R9 + MOVQ flag+32(FP), CX + MOVQ rounds+40(FP), BX + + MOVQ SP, BP + MOVQ SP, R10 + ADDQ $15, R10 + ANDQ $~15, R10 + MOVQ R10, SP + + VMOVDQU ·AVX_c40<>(SB), X0 + VMOVDQU ·AVX_c48<>(SB), X1 + VMOVDQA X0, X8 + VMOVDQA X1, X9 + + VMOVDQU ·AVX_iv3<>(SB), X0 + VMOVDQA X0, 0(SP) + XORQ CX, 0(SP) // 0(SP) = ·AVX_iv3 ^ (CX || 0) + + VMOVDQU 0(AX), X10 + VMOVDQU 16(AX), X11 + VMOVDQU 32(AX), X2 + VMOVDQU 48(AX), X3 + + VMOVQ_R8_X15 + VPINSRQ_1_R9_X15 + + VMOVDQA X10, X0 + VMOVDQA X11, X1 + VMOVDQU ·AVX_iv0<>(SB), X4 + VMOVDQU ·AVX_iv1<>(SB), X5 + VMOVDQU ·AVX_iv2<>(SB), X6 + + VPXOR X15, X6, X6 + VMOVDQA 0(SP), X7 + +loop: + SUBQ $1, BX; JCS done + LOAD_MSG_AVX_0_2_4_6_1_3_5_7() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX_1_0_11_5_12_2_7_3() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX_11_12_5_15_8_0_2_13() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX_2_5_4_15_6_10_0_8() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX_9_5_2_10_0_7_4_15() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX_2_6_0_8_12_10_11_3() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX_0_6_9_8_7_3_2_11() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX_5_15_8_2_0_4_6_10() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX_6_14_11_0_15_9_3_8() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX_12_13_1_10_2_7_4_5() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + SUBQ $1, BX; JCS done + LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5) + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX() + LOAD_MSG_AVX_15_9_3_13_11_14_12_0() + HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9) + SHUFFLE_AVX_INV() + + JMP loop + +done: + VMOVDQU 32(AX), X14 + VMOVDQU 48(AX), X15 + VPXOR X0, X10, X10 + VPXOR X1, X11, X11 + VPXOR X2, X14, X14 + VPXOR X3, X15, X15 + VPXOR X4, X10, X10 + VPXOR X5, X11, X11 + VPXOR X6, X14, X2 + VPXOR X7, X15, X3 + VMOVDQU X2, 32(AX) + VMOVDQU X3, 48(AX) + + VMOVDQU X10, 0(AX) + VMOVDQU X11, 16(AX) + VZEROUPPER + + MOVQ BP, SP + RET diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_amd64.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_amd64.go new file mode 100644 index 0000000000..4dbe90da8f --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_amd64.go @@ -0,0 +1,24 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.7,amd64,!gccgo,!appengine + +package blake2b + +import "golang.org/x/sys/cpu" + +func init() { + useSSE4 = cpu.X86.HasSSE41 +} + +//go:noescape +func fSSE4(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) + +func f(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) { + if useSSE4 { + fSSE4(h, m, c0, c1, flag, rounds) + } else { + fGeneric(h, m, c0, c1, flag, rounds) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_amd64.s b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_amd64.s new file mode 100644 index 0000000000..ce4b56d105 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_amd64.s @@ -0,0 +1,253 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +#include "textflag.h" + +DATA ·iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908 +DATA ·iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b +GLOBL ·iv0<>(SB), (NOPTR+RODATA), $16 + +DATA ·iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b +DATA ·iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1 +GLOBL ·iv1<>(SB), (NOPTR+RODATA), $16 + +DATA ·iv2<>+0x00(SB)/8, $0x510e527fade682d1 +DATA ·iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f +GLOBL ·iv2<>(SB), (NOPTR+RODATA), $16 + +DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b +DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179 +GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16 + +DATA ·c40<>+0x00(SB)/8, $0x0201000706050403 +DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b +GLOBL ·c40<>(SB), (NOPTR+RODATA), $16 + +DATA ·c48<>+0x00(SB)/8, $0x0100070605040302 +DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a +GLOBL ·c48<>(SB), (NOPTR+RODATA), $16 + +#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \ + MOVO v4, t1; \ + MOVO v5, v4; \ + MOVO t1, v5; \ + MOVO v6, t1; \ + PUNPCKLQDQ v6, t2; \ + PUNPCKHQDQ v7, v6; \ + PUNPCKHQDQ t2, v6; \ + PUNPCKLQDQ v7, t2; \ + MOVO t1, v7; \ + MOVO v2, t1; \ + PUNPCKHQDQ t2, v7; \ + PUNPCKLQDQ v3, t2; \ + PUNPCKHQDQ t2, v2; \ + PUNPCKLQDQ t1, t2; \ + PUNPCKHQDQ t2, v3 + +#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \ + MOVO v4, t1; \ + MOVO v5, v4; \ + MOVO t1, v5; \ + MOVO v2, t1; \ + PUNPCKLQDQ v2, t2; \ + PUNPCKHQDQ v3, v2; \ + PUNPCKHQDQ t2, v2; \ + PUNPCKLQDQ v3, t2; \ + MOVO t1, v3; \ + MOVO v6, t1; \ + PUNPCKHQDQ t2, v3; \ + PUNPCKLQDQ v7, t2; \ + PUNPCKHQDQ t2, v6; \ + PUNPCKLQDQ t1, t2; \ + PUNPCKHQDQ t2, v7 + +#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \ + PADDQ m0, v0; \ + PADDQ m1, v1; \ + PADDQ v2, v0; \ + PADDQ v3, v1; \ + PXOR v0, v6; \ + PXOR v1, v7; \ + PSHUFD $0xB1, v6, v6; \ + PSHUFD $0xB1, v7, v7; \ + PADDQ v6, v4; \ + PADDQ v7, v5; \ + PXOR v4, v2; \ + PXOR v5, v3; \ + PSHUFB c40, v2; \ + PSHUFB c40, v3; \ + PADDQ m2, v0; \ + PADDQ m3, v1; \ + PADDQ v2, v0; \ + PADDQ v3, v1; \ + PXOR v0, v6; \ + PXOR v1, v7; \ + PSHUFB c48, v6; \ + PSHUFB c48, v7; \ + PADDQ v6, v4; \ + PADDQ v7, v5; \ + PXOR v4, v2; \ + PXOR v5, v3; \ + MOVOU v2, t0; \ + PADDQ v2, t0; \ + PSRLQ $63, v2; \ + PXOR t0, v2; \ + MOVOU v3, t0; \ + PADDQ v3, t0; \ + PSRLQ $63, v3; \ + PXOR t0, v3 + +#define LOAD_MSG(m0, m1, m2, m3, i0, i1, i2, i3, i4, i5, i6, i7) \ + MOVQ i0*8(SI), m0; \ + PINSRQ $1, i1*8(SI), m0; \ + MOVQ i2*8(SI), m1; \ + PINSRQ $1, i3*8(SI), m1; \ + MOVQ i4*8(SI), m2; \ + PINSRQ $1, i5*8(SI), m2; \ + MOVQ i6*8(SI), m3; \ + PINSRQ $1, i7*8(SI), m3 + +// func fSSE4(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) +TEXT ·fSSE4(SB), 4, $24-48 // frame size = 8 + 16 byte alignment + MOVQ h+0(FP), AX + MOVQ m+8(FP), SI + MOVQ c0+16(FP), R8 + MOVQ c1+24(FP), R9 + MOVQ flag+32(FP), CX + MOVQ rounds+40(FP), BX + + MOVQ SP, BP + MOVQ SP, R10 + ADDQ $15, R10 + ANDQ $~15, R10 + MOVQ R10, SP + + MOVOU ·iv3<>(SB), X0 + MOVO X0, 0(SP) + XORQ CX, 0(SP) // 0(SP) = ·iv3 ^ (CX || 0) + + MOVOU ·c40<>(SB), X13 + MOVOU ·c48<>(SB), X14 + + MOVOU 0(AX), X12 + MOVOU 16(AX), X15 + + MOVQ R8, X8 + PINSRQ $1, R9, X8 + + MOVO X12, X0 + MOVO X15, X1 + MOVOU 32(AX), X2 + MOVOU 48(AX), X3 + MOVOU ·iv0<>(SB), X4 + MOVOU ·iv1<>(SB), X5 + MOVOU ·iv2<>(SB), X6 + + PXOR X8, X6 + MOVO 0(SP), X7 + +loop: + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 0, 2, 4, 6, 1, 3, 5, 7) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 8, 10, 12, 14, 9, 11, 13, 15) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 14, 4, 9, 13, 10, 8, 15, 6) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 1, 0, 11, 5, 12, 2, 7, 3) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 11, 12, 5, 15, 8, 0, 2, 13) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 10, 3, 7, 9, 14, 6, 1, 4) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 7, 3, 13, 11, 9, 1, 12, 14) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 2, 5, 4, 15, 6, 10, 0, 8) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 9, 5, 2, 10, 0, 7, 4, 15) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 14, 11, 6, 3, 1, 12, 8, 13) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 2, 6, 0, 8, 12, 10, 11, 3) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 4, 7, 15, 1, 13, 5, 14, 9) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 12, 1, 14, 4, 5, 15, 13, 10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 0, 6, 9, 8, 7, 3, 2, 11) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 13, 7, 12, 3, 11, 14, 1, 9) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 5, 15, 8, 2, 0, 4, 6, 10) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 6, 14, 11, 0, 15, 9, 3, 8) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 12, 13, 1, 10, 2, 7, 4, 5) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + SUBQ $1, BX; JCS done + LOAD_MSG(X8, X9, X10, X11, 10, 8, 7, 1, 2, 4, 6, 5) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9) + LOAD_MSG(X8, X9, X10, X11, 15, 9, 3, 13, 11, 14, 12, 0) + HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14) + SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9) + + JMP loop + +done: + MOVOU 32(AX), X10 + MOVOU 48(AX), X11 + PXOR X0, X12 + PXOR X1, X15 + PXOR X2, X10 + PXOR X3, X11 + PXOR X4, X12 + PXOR X5, X15 + PXOR X6, X10 + PXOR X7, X11 + MOVOU X10, 32(AX) + MOVOU X11, 48(AX) + + MOVOU X12, 0(AX) + MOVOU X15, 16(AX) + + MOVQ BP, SP + RET diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_f_fuzz.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_f_fuzz.go new file mode 100644 index 0000000000..ab73342803 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_f_fuzz.go @@ -0,0 +1,57 @@ +// +build gofuzz + +package blake2b + +import ( + "encoding/binary" +) + +func Fuzz(data []byte) int { + // Make sure the data confirms to the input model + if len(data) != 211 { + return 0 + } + // Parse everything and call all the implementations + var ( + rounds = binary.BigEndian.Uint16(data[0:2]) + + h [8]uint64 + m [16]uint64 + t [2]uint64 + f uint64 + ) + for i := 0; i < 8; i++ { + offset := 2 + i*8 + h[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) + } + for i := 0; i < 16; i++ { + offset := 66 + i*8 + m[i] = binary.LittleEndian.Uint64(data[offset : offset+8]) + } + t[0] = binary.LittleEndian.Uint64(data[194:202]) + t[1] = binary.LittleEndian.Uint64(data[202:210]) + + if data[210]%2 == 1 { // Avoid spinning the fuzzer to hit 0/1 + f = 0xFFFFFFFFFFFFFFFF + } + // Run the blake2b compression on all instruction sets and cross reference + want := h + fGeneric(&want, &m, t[0], t[1], f, uint64(rounds)) + + have := h + fSSE4(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("SSE4 mismatches generic algo") + } + have = h + fAVX(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("AVX mismatches generic algo") + } + have = h + fAVX2(&have, &m, t[0], t[1], f, uint64(rounds)) + if have != want { + panic("AVX2 mismatches generic algo") + } + return 1 +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_generic.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_generic.go new file mode 100644 index 0000000000..35c40cc924 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_generic.go @@ -0,0 +1,180 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package blake2b + +import ( + "encoding/binary" + "math/bits" +) + +// the precomputed values for BLAKE2b +// there are 10 16-byte arrays - one for each round +// the entries are calculated from the sigma constants. +var precomputed = [10][16]byte{ + {0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15}, + {14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3}, + {11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4}, + {7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8}, + {9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13}, + {2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9}, + {12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11}, + {13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10}, + {6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5}, + {10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0}, +} + +func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { + var m [16]uint64 + c0, c1 := c[0], c[1] + + for i := 0; i < len(blocks); { + c0 += BlockSize + if c0 < BlockSize { + c1++ + } + for j := range m { + m[j] = binary.LittleEndian.Uint64(blocks[i:]) + i += 8 + } + fGeneric(h, &m, c0, c1, flag, 12) + } + c[0], c[1] = c0, c1 +} + +func fGeneric(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) { + v0, v1, v2, v3, v4, v5, v6, v7 := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7] + v8, v9, v10, v11, v12, v13, v14, v15 := iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7] + v12 ^= c0 + v13 ^= c1 + v14 ^= flag + + for i := 0; i < int(rounds); i++ { + s := &(precomputed[i%10]) + + v0 += m[s[0]] + v0 += v4 + v12 ^= v0 + v12 = bits.RotateLeft64(v12, -32) + v8 += v12 + v4 ^= v8 + v4 = bits.RotateLeft64(v4, -24) + v1 += m[s[1]] + v1 += v5 + v13 ^= v1 + v13 = bits.RotateLeft64(v13, -32) + v9 += v13 + v5 ^= v9 + v5 = bits.RotateLeft64(v5, -24) + v2 += m[s[2]] + v2 += v6 + v14 ^= v2 + v14 = bits.RotateLeft64(v14, -32) + v10 += v14 + v6 ^= v10 + v6 = bits.RotateLeft64(v6, -24) + v3 += m[s[3]] + v3 += v7 + v15 ^= v3 + v15 = bits.RotateLeft64(v15, -32) + v11 += v15 + v7 ^= v11 + v7 = bits.RotateLeft64(v7, -24) + + v0 += m[s[4]] + v0 += v4 + v12 ^= v0 + v12 = bits.RotateLeft64(v12, -16) + v8 += v12 + v4 ^= v8 + v4 = bits.RotateLeft64(v4, -63) + v1 += m[s[5]] + v1 += v5 + v13 ^= v1 + v13 = bits.RotateLeft64(v13, -16) + v9 += v13 + v5 ^= v9 + v5 = bits.RotateLeft64(v5, -63) + v2 += m[s[6]] + v2 += v6 + v14 ^= v2 + v14 = bits.RotateLeft64(v14, -16) + v10 += v14 + v6 ^= v10 + v6 = bits.RotateLeft64(v6, -63) + v3 += m[s[7]] + v3 += v7 + v15 ^= v3 + v15 = bits.RotateLeft64(v15, -16) + v11 += v15 + v7 ^= v11 + v7 = bits.RotateLeft64(v7, -63) + + v0 += m[s[8]] + v0 += v5 + v15 ^= v0 + v15 = bits.RotateLeft64(v15, -32) + v10 += v15 + v5 ^= v10 + v5 = bits.RotateLeft64(v5, -24) + v1 += m[s[9]] + v1 += v6 + v12 ^= v1 + v12 = bits.RotateLeft64(v12, -32) + v11 += v12 + v6 ^= v11 + v6 = bits.RotateLeft64(v6, -24) + v2 += m[s[10]] + v2 += v7 + v13 ^= v2 + v13 = bits.RotateLeft64(v13, -32) + v8 += v13 + v7 ^= v8 + v7 = bits.RotateLeft64(v7, -24) + v3 += m[s[11]] + v3 += v4 + v14 ^= v3 + v14 = bits.RotateLeft64(v14, -32) + v9 += v14 + v4 ^= v9 + v4 = bits.RotateLeft64(v4, -24) + + v0 += m[s[12]] + v0 += v5 + v15 ^= v0 + v15 = bits.RotateLeft64(v15, -16) + v10 += v15 + v5 ^= v10 + v5 = bits.RotateLeft64(v5, -63) + v1 += m[s[13]] + v1 += v6 + v12 ^= v1 + v12 = bits.RotateLeft64(v12, -16) + v11 += v12 + v6 ^= v11 + v6 = bits.RotateLeft64(v6, -63) + v2 += m[s[14]] + v2 += v7 + v13 ^= v2 + v13 = bits.RotateLeft64(v13, -16) + v8 += v13 + v7 ^= v8 + v7 = bits.RotateLeft64(v7, -63) + v3 += m[s[15]] + v3 += v4 + v14 ^= v3 + v14 = bits.RotateLeft64(v14, -16) + v9 += v14 + v4 ^= v9 + v4 = bits.RotateLeft64(v4, -63) + } + h[0] ^= v0 ^ v8 + h[1] ^= v1 ^ v9 + h[2] ^= v2 ^ v10 + h[3] ^= v3 ^ v11 + h[4] ^= v4 ^ v12 + h[5] ^= v5 ^ v13 + h[6] ^= v6 ^ v14 + h[7] ^= v7 ^ v15 +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_ref.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_ref.go new file mode 100644 index 0000000000..9d0ade473a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2b_ref.go @@ -0,0 +1,11 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 appengine gccgo + +package blake2b + +func f(h *[8]uint64, m *[16]uint64, c0, c1 uint64, flag uint64, rounds uint64) { + fGeneric(h, m, c0, c1, flag, rounds) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2x.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2x.go new file mode 100644 index 0000000000..52c414db0e --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/blake2x.go @@ -0,0 +1,177 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package blake2b + +import ( + "encoding/binary" + "errors" + "io" +) + +// XOF defines the interface to hash functions that +// support arbitrary-length output. +type XOF interface { + // Write absorbs more data into the hash's state. It panics if called + // after Read. + io.Writer + + // Read reads more output from the hash. It returns io.EOF if the limit + // has been reached. + io.Reader + + // Clone returns a copy of the XOF in its current state. + Clone() XOF + + // Reset resets the XOF to its initial state. + Reset() +} + +// OutputLengthUnknown can be used as the size argument to NewXOF to indicate +// the length of the output is not known in advance. +const OutputLengthUnknown = 0 + +// magicUnknownOutputLength is a magic value for the output size that indicates +// an unknown number of output bytes. +const magicUnknownOutputLength = (1 << 32) - 1 + +// maxOutputLength is the absolute maximum number of bytes to produce when the +// number of output bytes is unknown. +const maxOutputLength = (1 << 32) * 64 + +// NewXOF creates a new variable-output-length hash. The hash either produce a +// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes +// (size == OutputLengthUnknown). In the latter case, an absolute limit of +// 256GiB applies. +// +// A non-nil key turns the hash into a MAC. The key must between +// zero and 32 bytes long. +func NewXOF(size uint32, key []byte) (XOF, error) { + if len(key) > Size { + return nil, errKeySize + } + if size == magicUnknownOutputLength { + // 2^32-1 indicates an unknown number of bytes and thus isn't a + // valid length. + return nil, errors.New("blake2b: XOF length too large") + } + if size == OutputLengthUnknown { + size = magicUnknownOutputLength + } + x := &xof{ + d: digest{ + size: Size, + keyLen: len(key), + }, + length: size, + } + copy(x.d.key[:], key) + x.Reset() + return x, nil +} + +type xof struct { + d digest + length uint32 + remaining uint64 + cfg, root, block [Size]byte + offset int + nodeOffset uint32 + readMode bool +} + +func (x *xof) Write(p []byte) (n int, err error) { + if x.readMode { + panic("blake2b: write to XOF after read") + } + return x.d.Write(p) +} + +func (x *xof) Clone() XOF { + clone := *x + return &clone +} + +func (x *xof) Reset() { + x.cfg[0] = byte(Size) + binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length + binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length + x.cfg[17] = byte(Size) // inner hash size + + x.d.Reset() + x.d.h[1] ^= uint64(x.length) << 32 + + x.remaining = uint64(x.length) + if x.remaining == magicUnknownOutputLength { + x.remaining = maxOutputLength + } + x.offset, x.nodeOffset = 0, 0 + x.readMode = false +} + +func (x *xof) Read(p []byte) (n int, err error) { + if !x.readMode { + x.d.finalize(&x.root) + x.readMode = true + } + + if x.remaining == 0 { + return 0, io.EOF + } + + n = len(p) + if uint64(n) > x.remaining { + n = int(x.remaining) + p = p[:n] + } + + if x.offset > 0 { + blockRemaining := Size - x.offset + if n < blockRemaining { + x.offset += copy(p, x.block[x.offset:]) + x.remaining -= uint64(n) + return + } + copy(p, x.block[x.offset:]) + p = p[blockRemaining:] + x.offset = 0 + x.remaining -= uint64(blockRemaining) + } + + for len(p) >= Size { + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + copy(p, x.block[:]) + p = p[Size:] + x.remaining -= uint64(Size) + } + + if todo := len(p); todo > 0 { + if x.remaining < uint64(Size) { + x.cfg[0] = byte(x.remaining) + } + binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset) + x.nodeOffset++ + + x.d.initConfig(&x.cfg) + x.d.Write(x.root[:]) + x.d.finalize(&x.block) + + x.offset = copy(p, x.block[:todo]) + x.remaining -= uint64(todo) + } + return +} + +func (d *digest) initConfig(cfg *[Size]byte) { + d.offset, d.c[0], d.c[1] = 0, 0, 0 + for i := range d.h { + d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:]) + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/register.go b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/register.go new file mode 100644 index 0000000000..efd689af4b --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/crypto/blake2b/register.go @@ -0,0 +1,32 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package blake2b + +import ( + "crypto" + "hash" +) + +func init() { + newHash256 := func() hash.Hash { + h, _ := New256(nil) + return h + } + newHash384 := func() hash.Hash { + h, _ := New384(nil) + return h + } + + newHash512 := func() hash.Hash { + h, _ := New512(nil) + return h + } + + crypto.RegisterHash(crypto.BLAKE2b_256, newHash256) + crypto.RegisterHash(crypto.BLAKE2b_384, newHash384) + crypto.RegisterHash(crypto.BLAKE2b_512, newHash512) +} diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go index 4567fafc72..2869b4c191 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/crypto.go @@ -34,6 +34,15 @@ import ( "golang.org/x/crypto/sha3" ) +//SignatureLength indicates the byte length required to carry a signature with recovery id. +const SignatureLength = 64 + 1 // 64 bytes ECDSA signature + 1 byte recovery id + +// RecoveryIDOffset points to the byte offset within the signature that contains the recovery id. +const RecoveryIDOffset = 64 + +// DigestLength sets the signature digest exact length +const DigestLength = 32 + var ( secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go b/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go index aadf028d26..1fe84509e7 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/signature_cgo.go @@ -47,24 +47,24 @@ func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { // // This function is susceptible to chosen plaintext attacks that can leak // information about the private key that is used for signing. Callers must -// be aware that the given hash cannot be chosen by an adversery. Common +// be aware that the given digest cannot be chosen by an adversery. Common // solution is to hash any input before calculating the signature. // // The produced signature is in the [R || S || V] format where V is 0 or 1. -func Sign(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { - if len(hash) != 32 { - return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) +func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { + if len(digestHash) != DigestLength { + return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash)) } seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8) defer zeroBytes(seckey) - return secp256k1.Sign(hash, seckey) + return secp256k1.Sign(digestHash, seckey) } -// VerifySignature checks that the given public key created signature over hash. +// VerifySignature checks that the given public key created signature over digest. // The public key should be in compressed (33 bytes) or uncompressed (65 bytes) format. // The signature should have the 64 byte [R || S] format. -func VerifySignature(pubkey, hash, signature []byte) bool { - return secp256k1.VerifySignature(pubkey, hash, signature) +func VerifySignature(pubkey, digestHash, signature []byte) bool { + return secp256k1.VerifySignature(pubkey, digestHash, signature) } // DecompressPubkey parses a public key in the 33-byte compressed format. diff --git a/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go b/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go index 90d072cda7..067d32e13c 100644 --- a/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go +++ b/vendor/github.com/ethereum/go-ethereum/crypto/signature_nocgo.go @@ -41,7 +41,7 @@ func Ecrecover(hash, sig []byte) ([]byte, error) { // SigToPub returns the public key that created the given signature. func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) { // Convert to btcec input format with 'recovery id' v at the beginning. - btcsig := make([]byte, 65) + btcsig := make([]byte, SignatureLength) btcsig[0] = sig[64] + 27 copy(btcsig[1:], sig) diff --git a/vendor/github.com/ethereum/go-ethereum/dashboard/README.md b/vendor/github.com/ethereum/go-ethereum/dashboard/README.md index 641c5f44bc..67b65bda36 100644 --- a/vendor/github.com/ethereum/go-ethereum/dashboard/README.md +++ b/vendor/github.com/ethereum/go-ethereum/dashboard/README.md @@ -48,8 +48,8 @@ For more IDE support install the `linter-eslint` package too, which finds the `. [ESLint]: https://eslint.org/ [Airbnb]: https://github.com/airbnb/javascript/tree/master/react [Webpack]: https://webpack.github.io/ -[WA]: http://webpack.github.io/analyse/ -[WV]: http://chrisbateman.github.io/webpack-visualizer/ +[WA]: https://webpack.github.io/analyse/ +[WV]: https://chrisbateman.github.io/webpack-visualizer/ [Node.js]: https://nodejs.org/en/ [Flow]: https://flow.org/ [Atom]: https://atom.io/ diff --git a/vendor/github.com/ethereum/go-ethereum/dashboard/dashboard.go b/vendor/github.com/ethereum/go-ethereum/dashboard/dashboard.go index d69a750f10..b576293bc9 100644 --- a/vendor/github.com/ethereum/go-ethereum/dashboard/dashboard.go +++ b/vendor/github.com/ethereum/go-ethereum/dashboard/dashboard.go @@ -125,7 +125,7 @@ func (db *Dashboard) APIs() []rpc.API { return nil } // Start starts the data collection thread and the listening server of the dashboard. // Implements the node.Service interface. func (db *Dashboard) Start(server *p2p.Server) error { - log.Info("Starting dashboard") + log.Info("Starting dashboard", "url", fmt.Sprintf("http://%s:%d", db.config.Host, db.config.Port)) db.wg.Add(3) go db.collectSystemData() diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api.go b/vendor/github.com/ethereum/go-ethereum/eth/api.go index 98c2f5874f..f8c51c09bd 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api.go @@ -168,6 +168,11 @@ func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI { // ExportChain exports the current blockchain into a local file. func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) { + if _, err := os.Stat(file); err == nil { + // File already exists. Allowing overwrite could be a DoS vecotor, + // since the 'file' may point to arbitrary paths on the drive + return false, errors.New("location would overwrite an existing file") + } // Make sure we can create the file to export into out, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) if err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go index 69904a70f2..4b74ccff51 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/api_backend.go @@ -72,6 +72,23 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil } +func (b *EthAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return b.HeaderByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header := b.eth.blockchain.GetHeaderByHash(hash) + if header == nil { + return nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { + return nil, errors.New("hash is not currently canonical") + } + return header, nil + } + return nil, errors.New("invalid arguments; neither block nor hash specified") +} + func (b *EthAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { return b.eth.blockchain.GetHeaderByHash(hash), nil } @@ -93,6 +110,27 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ return b.eth.blockchain.GetBlockByHash(hash), nil } +func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return b.BlockByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header := b.eth.blockchain.GetHeaderByHash(hash) + if header == nil { + return nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { + return nil, errors.New("hash is not currently canonical") + } + block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64()) + if block == nil { + return nil, errors.New("header found, but block body is missing") + } + return block, nil + } + return nil, errors.New("invalid arguments; neither block nor hash specified") +} + func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { // Pending state is only known by the miner if number == rpc.PendingBlockNumber { @@ -111,6 +149,27 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B return stateDb, header, err } +func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return b.StateAndHeaderByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header, err := b.HeaderByHash(ctx, hash) + if err != nil { + return nil, nil, err + } + if header == nil { + return nil, nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { + return nil, nil, errors.New("hash is not currently canonical") + } + stateDb, err := b.eth.BlockChain().StateAt(header.Root) + return stateDb, header, err + } + return nil, nil, errors.New("invalid arguments; neither block nor hash specified") +} + func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { return b.eth.blockchain.GetReceiptsByHash(hash), nil } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/backend.go b/vendor/github.com/ethereum/go-ethereum/eth/backend.go index dc4ff8ade8..83e05e96a8 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/backend.go @@ -69,8 +69,6 @@ type Ethereum struct { // Channel for shutting down the service shutdownChan chan bool - server *p2p.Server - // Handlers txPool *core.TxPool blockchain *core.BlockChain @@ -137,7 +135,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideIstanbul) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/config.go b/vendor/github.com/ethereum/go-ethereum/eth/config.go index 6887872276..5094a533bf 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/config.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/config.go @@ -154,4 +154,7 @@ type Config struct { // CheckpointOracle is the configuration for checkpoint oracle. CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"` + + // Istanbul block override (TODO: remove after the fork) + OverrideIstanbul *big.Int } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go index edd0eb4d95..f8982f696f 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/downloader.go @@ -1574,13 +1574,14 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error { func (d *Downloader) processFastSyncContent(latest *types.Header) error { // Start syncing state of the reported head block. This should get us most of // the state of the pivot block. - stateSync := d.syncState(latest.Root) - defer stateSync.Cancel() - go func() { - if err := stateSync.Wait(); err != nil && err != errCancelStateFetch && err != errCanceled { + sync := d.syncState(latest.Root) + defer sync.Cancel() + closeOnErr := func(s *stateSync) { + if err := s.Wait(); err != nil && err != errCancelStateFetch && err != errCanceled { d.queue.Close() // wake up Results } - }() + } + go closeOnErr(sync) // Figure out the ideal pivot block. Note, that this goalpost may move if the // sync takes long enough for the chain head to move significantly. pivot := uint64(0) @@ -1600,12 +1601,12 @@ func (d *Downloader) processFastSyncContent(latest *types.Header) error { if len(results) == 0 { // If pivot sync is done, stop if oldPivot == nil { - return stateSync.Cancel() + return sync.Cancel() } // If sync failed, stop select { case <-d.cancelCh: - stateSync.Cancel() + sync.Cancel() return errCanceled default: } @@ -1625,28 +1626,24 @@ func (d *Downloader) processFastSyncContent(latest *types.Header) error { } } P, beforeP, afterP := splitAroundPivot(pivot, results) - if err := d.commitFastSyncData(beforeP, stateSync); err != nil { + if err := d.commitFastSyncData(beforeP, sync); err != nil { return err } if P != nil { // If new pivot block found, cancel old state retrieval and restart if oldPivot != P { - stateSync.Cancel() + sync.Cancel() - stateSync = d.syncState(P.Header.Root) - defer stateSync.Cancel() - go func() { - if err := stateSync.Wait(); err != nil && err != errCancelStateFetch && err != errCanceled { - d.queue.Close() // wake up Results - } - }() + sync = d.syncState(P.Header.Root) + defer sync.Cancel() + go closeOnErr(sync) oldPivot = P } // Wait for completion, occasionally checking for pivot staleness select { - case <-stateSync.done: - if stateSync.err != nil { - return stateSync.err + case <-sync.done: + if sync.err != nil { + return sync.err } if err := d.commitPivotBlock(P); err != nil { return err diff --git a/vendor/github.com/ethereum/go-ethereum/eth/downloader/statesync.go b/vendor/github.com/ethereum/go-ethereum/eth/downloader/statesync.go index b422557d58..f875b3a84c 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/downloader/statesync.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/downloader/statesync.go @@ -347,7 +347,7 @@ func (s *stateSync) commit(force bool) error { } start := time.Now() b := s.d.stateDB.NewBatch() - if written, err := s.sched.Commit(b); written == 0 || err != nil { + if err := s.sched.Commit(b); err != nil { return err } if err := b.Write(); err != nil { diff --git a/vendor/github.com/ethereum/go-ethereum/eth/handler.go b/vendor/github.com/ethereum/go-ethereum/eth/handler.go index 4ce2d1c82f..d2355a8768 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/handler.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/handler.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/fetcher" @@ -63,7 +64,8 @@ func errResp(code errCode, format string, v ...interface{}) error { } type ProtocolManager struct { - networkID uint64 + networkID uint64 + forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) @@ -103,6 +105,7 @@ func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCh // Create the protocol manager with the base fields manager := &ProtocolManager{ networkID: networkID, + forkFilter: forkid.NewFilter(blockchain), eventMux: mux, txpool: txpool, blockchain: blockchain, @@ -304,7 +307,7 @@ func (pm *ProtocolManager) handle(p *peer) error { number = head.Number.Uint64() td = pm.blockchain.GetTd(hash, number) ) - if err := p.Handshake(pm.networkID, td, hash, genesis.Hash()); err != nil { + if err := p.Handshake(pm.networkID, td, hash, genesis.Hash(), forkid.NewID(pm.blockchain), pm.forkFilter); err != nil { p.Log().Debug("Ethereum handshake failed", "err", err) return err } diff --git a/vendor/github.com/ethereum/go-ethereum/eth/peer.go b/vendor/github.com/ethereum/go-ethereum/eth/peer.go index 814c787b8c..0beec1d844 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/peer.go @@ -25,6 +25,7 @@ import ( mapset "github.com/deckarep/golang-set" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" @@ -353,22 +354,46 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error { // Handshake executes the eth protocol handshake, negotiating version number, // network IDs, difficulties, head and genesis blocks. -func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash) error { +func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { // Send out own handshake in a new thread errc := make(chan error, 2) - var status statusData // safe to read after two values have been received from errc + var ( + status63 statusData63 // safe to read after two values have been received from errc + status statusData // safe to read after two values have been received from errc + ) go func() { - errc <- p2p.Send(p.rw, StatusMsg, &statusData{ - ProtocolVersion: uint32(p.version), - NetworkId: network, - TD: td, - CurrentBlock: head, - GenesisBlock: genesis, - }) + switch { + case p.version == eth63: + errc <- p2p.Send(p.rw, StatusMsg, &statusData63{ + ProtocolVersion: uint32(p.version), + NetworkId: network, + TD: td, + CurrentBlock: head, + GenesisBlock: genesis, + }) + case p.version == eth64: + errc <- p2p.Send(p.rw, StatusMsg, &statusData{ + ProtocolVersion: uint32(p.version), + NetworkID: network, + TD: td, + Head: head, + Genesis: genesis, + ForkID: forkID, + }) + default: + panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) + } }() go func() { - errc <- p.readStatus(network, &status, genesis) + switch { + case p.version == eth63: + errc <- p.readStatusLegacy(network, &status63, genesis) + case p.version == eth64: + errc <- p.readStatus(network, &status, genesis, forkFilter) + default: + panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) + } }() timeout := time.NewTimer(handshakeTimeout) defer timeout.Stop() @@ -382,11 +407,18 @@ func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis return p2p.DiscReadTimeout } } - p.td, p.head = status.TD, status.CurrentBlock + switch { + case p.version == eth63: + p.td, p.head = status63.TD, status63.CurrentBlock + case p.version == eth64: + p.td, p.head = status.TD, status.Head + default: + panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version)) + } return nil } -func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash) (err error) { +func (p *peer) readStatusLegacy(network uint64, status *statusData63, genesis common.Hash) error { msg, err := p.rw.ReadMsg() if err != nil { return err @@ -402,10 +434,10 @@ func (p *peer) readStatus(network uint64, status *statusData, genesis common.Has return errResp(ErrDecode, "msg %v: %v", msg, err) } if status.GenesisBlock != genesis { - return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8]) + return errResp(ErrGenesisMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8]) } if status.NetworkId != network { - return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network) + return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkId, network) } if int(status.ProtocolVersion) != p.version { return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) @@ -413,6 +445,36 @@ func (p *peer) readStatus(network uint64, status *statusData, genesis common.Has return nil } +func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash, forkFilter forkid.Filter) error { + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + if msg.Code != StatusMsg { + return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) + } + if msg.Size > protocolMaxMsgSize { + return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize) + } + // Decode the handshake and make sure everything matches + if err := msg.Decode(&status); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + if status.NetworkID != network { + return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkID, network) + } + if int(status.ProtocolVersion) != p.version { + return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version) + } + if status.Genesis != genesis { + return errResp(ErrGenesisMismatch, "%x (!= %x)", status.Genesis, genesis) + } + if err := forkFilter(status.ForkID); err != nil { + return errResp(ErrForkIDRejected, "%v", err) + } + return nil +} + // String implements fmt.Stringer. func (p *peer) String() string { return fmt.Sprintf("Peer %s [%s]", p.id, diff --git a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go index de0c979d89..62e4d13d14 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/protocol.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/forkid" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rlp" @@ -30,24 +31,23 @@ import ( // Constants to match up protocol versions and messages const ( - eth62 = 62 eth63 = 63 + eth64 = 64 ) // protocolName is the official short name of the protocol used during capability negotiation. const protocolName = "eth" // ProtocolVersions are the supported versions of the eth protocol (first is primary). -var ProtocolVersions = []uint{eth63} +var ProtocolVersions = []uint{eth64, eth63} // protocolLengths are the number of implemented message corresponding to different protocol versions. -var protocolLengths = map[uint]uint64{eth63: 17, eth62: 8} +var protocolLengths = map[uint]uint64{eth64: 17, eth63: 17} const protocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message // eth protocol message codes const ( - // Protocol messages belonging to eth/62 StatusMsg = 0x00 NewBlockHashesMsg = 0x01 TxMsg = 0x02 @@ -56,12 +56,10 @@ const ( GetBlockBodiesMsg = 0x05 BlockBodiesMsg = 0x06 NewBlockMsg = 0x07 - - // Protocol messages belonging to eth/63 - GetNodeDataMsg = 0x0d - NodeDataMsg = 0x0e - GetReceiptsMsg = 0x0f - ReceiptsMsg = 0x10 + GetNodeDataMsg = 0x0d + NodeDataMsg = 0x0e + GetReceiptsMsg = 0x0f + ReceiptsMsg = 0x10 ) type errCode int @@ -71,11 +69,11 @@ const ( ErrDecode ErrInvalidMsgCode ErrProtocolVersionMismatch - ErrNetworkIdMismatch - ErrGenesisBlockMismatch + ErrNetworkIDMismatch + ErrGenesisMismatch + ErrForkIDRejected ErrNoStatusMsg ErrExtraStatusMsg - ErrSuspendedPeer ) func (e errCode) String() string { @@ -88,11 +86,11 @@ var errorToString = map[int]string{ ErrDecode: "Invalid message", ErrInvalidMsgCode: "Invalid message code", ErrProtocolVersionMismatch: "Protocol version mismatch", - ErrNetworkIdMismatch: "NetworkId mismatch", - ErrGenesisBlockMismatch: "Genesis block mismatch", + ErrNetworkIDMismatch: "Network ID mismatch", + ErrGenesisMismatch: "Genesis mismatch", + ErrForkIDRejected: "Fork ID rejected", ErrNoStatusMsg: "No status message", ErrExtraStatusMsg: "Extra status message", - ErrSuspendedPeer: "Suspended peer", } type txPool interface { @@ -108,8 +106,8 @@ type txPool interface { SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription } -// statusData is the network packet for the status message. -type statusData struct { +// statusData63 is the network packet for the status message for eth/63. +type statusData63 struct { ProtocolVersion uint32 NetworkId uint64 TD *big.Int @@ -117,6 +115,16 @@ type statusData struct { GenesisBlock common.Hash } +// statusData is the network packet for the status message for eth/64 and later. +type statusData struct { + ProtocolVersion uint32 + NetworkID uint64 + TD *big.Int + Head common.Hash + Genesis common.Hash + ForkID forkid.ID +} + // newBlockHashesData is the network packet for the block announcements. type newBlockHashesData []struct { Hash common.Hash // Hash of one particular block being announced diff --git a/vendor/github.com/ethereum/go-ethereum/eth/tracers/tracer.go b/vendor/github.com/ethereum/go-ethereum/eth/tracers/tracer.go index 9d6701868c..724c5443a6 100644 --- a/vendor/github.com/ethereum/go-ethereum/eth/tracers/tracer.go +++ b/vendor/github.com/ethereum/go-ethereum/eth/tracers/tracer.go @@ -99,7 +99,7 @@ func (mw *memoryWrapper) slice(begin, end int64) []byte { log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin) return nil } - return mw.memory.Get(begin, end-begin) + return mw.memory.GetCopy(begin, end-begin) } // getUint returns the 32 bytes at the specified address interpreted as a uint. @@ -390,7 +390,7 @@ func New(code string) (*Tracer, error) { return 1 }) tracer.vm.PushGlobalGoFunction("isPrecompiled", func(ctx *duktape.Context) int { - _, ok := vm.PrecompiledContractsByzantium[common.BytesToAddress(popSlice(ctx))] + _, ok := vm.PrecompiledContractsIstanbul[common.BytesToAddress(popSlice(ctx))] ctx.PushBoolean(ok) return 1 }) diff --git a/vendor/github.com/ethereum/go-ethereum/ethdb/leveldb/leveldb.go b/vendor/github.com/ethereum/go-ethereum/ethdb/leveldb/leveldb.go index aba6593c7a..378d4c3cd2 100644 --- a/vendor/github.com/ethereum/go-ethereum/ethdb/leveldb/leveldb.go +++ b/vendor/github.com/ethereum/go-ethereum/ethdb/leveldb/leveldb.go @@ -62,14 +62,18 @@ type Database struct { fn string // filename for reporting db *leveldb.DB // LevelDB instance - compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction - compReadMeter metrics.Meter // Meter for measuring the data read during compaction - compWriteMeter metrics.Meter // Meter for measuring the data written during compaction - writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction - writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction - diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database - diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read - diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written + compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction + compReadMeter metrics.Meter // Meter for measuring the data read during compaction + compWriteMeter metrics.Meter // Meter for measuring the data written during compaction + writeDelayNMeter metrics.Meter // Meter for measuring the write delay number due to database compaction + writeDelayMeter metrics.Meter // Meter for measuring the write delay duration due to database compaction + diskSizeGauge metrics.Gauge // Gauge for tracking the size of all the levels in the database + diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read + diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written + memCompGauge metrics.Gauge // Gauge for tracking the number of memory compaction + level0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in level0 + nonlevel0CompGauge metrics.Gauge // Gauge for tracking the number of table compaction in non0 level + seekCompGauge metrics.Gauge // Gauge for tracking the number of table compaction caused by read opt quitLock sync.Mutex // Mutex protecting the quit channel access quitChan chan chan error // Quit channel to stop the metrics collection before closing the database @@ -96,6 +100,7 @@ func New(file string, cache int, handles int, namespace string) (*Database, erro BlockCacheCapacity: cache / 2 * opt.MiB, WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally Filter: filter.NewBloomFilter(10), + DisableSeeksCompaction: true, }) if _, corrupted := err.(*errors.ErrCorrupted); corrupted { db, err = leveldb.RecoverFile(file, nil) @@ -118,6 +123,10 @@ func New(file string, cache int, handles int, namespace string) (*Database, erro ldb.diskWriteMeter = metrics.NewRegisteredMeter(namespace+"disk/write", nil) ldb.writeDelayMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/duration", nil) ldb.writeDelayNMeter = metrics.NewRegisteredMeter(namespace+"compact/writedelay/counter", nil) + ldb.memCompGauge = metrics.NewRegisteredGauge(namespace+"compact/memory", nil) + ldb.level0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/level0", nil) + ldb.nonlevel0CompGauge = metrics.NewRegisteredGauge(namespace+"compact/nonlevel0", nil) + ldb.seekCompGauge = metrics.NewRegisteredGauge(namespace+"compact/seek", nil) // Start up the metrics gathering and return go ldb.meter(metricsGatheringInterval) @@ -375,6 +384,29 @@ func (db *Database) meter(refresh time.Duration) { } iostats[0], iostats[1] = nRead, nWrite + compCount, err := db.db.GetProperty("leveldb.compcount") + if err != nil { + db.log.Error("Failed to read database iostats", "err", err) + merr = err + continue + } + + var ( + memComp uint32 + level0Comp uint32 + nonLevel0Comp uint32 + seekComp uint32 + ) + if n, err := fmt.Sscanf(compCount, "MemComp:%d Level0Comp:%d NonLevel0Comp:%d SeekComp:%d", &memComp, &level0Comp, &nonLevel0Comp, &seekComp); n != 4 || err != nil { + db.log.Error("Compaction count statistic not found") + merr = err + continue + } + db.memCompGauge.Update(int64(memComp)) + db.level0CompGauge.Update(int64(level0Comp)) + db.nonlevel0CompGauge.Update(int64(nonLevel0Comp)) + db.seekCompGauge.Update(int64(seekComp)) + // Sleep a bit, then repeat the stats collection select { case errc = <-db.quitChan: diff --git a/vendor/github.com/ethereum/go-ethereum/graphql/graphiql.go b/vendor/github.com/ethereum/go-ethereum/graphql/graphiql.go index 483d4cea3b..864ebf57df 100644 --- a/vendor/github.com/ethereum/go-ethereum/graphql/graphiql.go +++ b/vendor/github.com/ethereum/go-ethereum/graphql/graphiql.go @@ -52,7 +52,7 @@ func (h GraphiQL) ServeHTTP(w http.ResponseWriter, r *http.Request) { respond(w, errorJSON("only GET requests are supported"), http.StatusMethodNotAllowed) return } - + w.Header().Set("Content-Type", "text/html") w.Write(graphiql) } diff --git a/vendor/github.com/ethereum/go-ethereum/graphql/graphql.go b/vendor/github.com/ethereum/go-ethereum/graphql/graphql.go index df279f42b1..ddd928dff1 100644 --- a/vendor/github.com/ethereum/go-ethereum/graphql/graphql.go +++ b/vendor/github.com/ethereum/go-ethereum/graphql/graphql.go @@ -36,20 +36,19 @@ import ( ) var ( - errOnlyOnMainChain = errors.New("this operation is only available for blocks on the canonical chain") - errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash") + errBlockInvariant = errors.New("block objects must be instantiated with at least one of num or hash") ) // Account represents an Ethereum account at a particular block. type Account struct { - backend ethapi.Backend - address common.Address - blockNumber rpc.BlockNumber + backend ethapi.Backend + address common.Address + blockNrOrHash rpc.BlockNumberOrHash } // getState fetches the StateDB object for an account. func (a *Account) getState(ctx context.Context) (*state.StateDB, error) { - state, _, err := a.backend.StateAndHeaderByNumber(ctx, a.blockNumber) + state, _, err := a.backend.StateAndHeaderByNumberOrHash(ctx, a.blockNrOrHash) return state, err } @@ -102,9 +101,9 @@ func (l *Log) Transaction(ctx context.Context) *Transaction { func (l *Log) Account(ctx context.Context, args BlockNumberArgs) *Account { return &Account{ - backend: l.backend, - address: l.log.Address, - blockNumber: args.Number(), + backend: l.backend, + address: l.log.Address, + blockNrOrHash: args.NumberOrLatest(), } } @@ -136,10 +135,10 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, error) { tx, blockHash, _, index := rawdb.ReadTransaction(t.backend.ChainDb(), t.hash) if tx != nil { t.tx = tx + blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false) t.block = &Block{ - backend: t.backend, - hash: blockHash, - canonical: unknown, + backend: t.backend, + numberOrHash: &blockNrOrHash, } t.index = index } else { @@ -203,9 +202,9 @@ func (t *Transaction) To(ctx context.Context, args BlockNumberArgs) (*Account, e return nil, nil } return &Account{ - backend: t.backend, - address: *to, - blockNumber: args.Number(), + backend: t.backend, + address: *to, + blockNrOrHash: args.NumberOrLatest(), }, nil } @@ -221,9 +220,9 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account, from, _ := types.Sender(signer, tx) return &Account{ - backend: t.backend, - address: from, - blockNumber: args.Number(), + backend: t.backend, + address: from, + blockNrOrHash: args.NumberOrLatest(), }, nil } @@ -293,9 +292,9 @@ func (t *Transaction) CreatedContract(ctx context.Context, args BlockNumberArgs) return nil, err } return &Account{ - backend: t.backend, - address: receipt.ContractAddress, - blockNumber: args.Number(), + backend: t.backend, + address: receipt.ContractAddress, + blockNrOrHash: args.NumberOrLatest(), }, nil } @@ -317,45 +316,16 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) { type BlockType int -const ( - unknown BlockType = iota - isCanonical - notCanonical -) - // Block represents an Ethereum block. -// backend, and either num or hash are mandatory. All other fields are lazily fetched +// backend, and numberOrHash are mandatory. All other fields are lazily fetched // when required. type Block struct { - backend ethapi.Backend - num *rpc.BlockNumber - hash common.Hash - header *types.Header - block *types.Block - receipts []*types.Receipt - canonical BlockType // Indicates if this block is on the main chain or not. -} - -func (b *Block) onMainChain(ctx context.Context) error { - if b.canonical == unknown { - header, err := b.resolveHeader(ctx) - if err != nil { - return err - } - canonHeader, err := b.backend.HeaderByNumber(ctx, rpc.BlockNumber(header.Number.Uint64())) - if err != nil { - return err - } - if header.Hash() == canonHeader.Hash() { - b.canonical = isCanonical - } else { - b.canonical = notCanonical - } - } - if b.canonical != isCanonical { - return errOnlyOnMainChain - } - return nil + backend ethapi.Backend + numberOrHash *rpc.BlockNumberOrHash + hash common.Hash + header *types.Header + block *types.Block + receipts []*types.Receipt } // resolve returns the internal Block object representing this block, fetching @@ -364,14 +334,17 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) { if b.block != nil { return b.block, nil } - var err error - if b.hash != (common.Hash{}) { - b.block, err = b.backend.BlockByHash(ctx, b.hash) - } else { - b.block, err = b.backend.BlockByNumber(ctx, *b.num) + if b.numberOrHash == nil { + latest := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) + b.numberOrHash = &latest } + var err error + b.block, err = b.backend.BlockByNumberOrHash(ctx, *b.numberOrHash) if b.block != nil && b.header == nil { b.header = b.block.Header() + if hash, ok := b.numberOrHash.Hash(); ok { + b.hash = hash + } } return b.block, err } @@ -380,7 +353,7 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) { // if necessary. Call this function instead of `resolve` unless you need the // additional data (transactions and uncles). func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { - if b.num == nil && b.hash == (common.Hash{}) { + if b.numberOrHash == nil && b.hash == (common.Hash{}) { return nil, errBlockInvariant } var err error @@ -388,7 +361,7 @@ func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { if b.hash != (common.Hash{}) { b.header, err = b.backend.HeaderByHash(ctx, b.hash) } else { - b.header, err = b.backend.HeaderByNumber(ctx, *b.num) + b.header, err = b.backend.HeaderByNumberOrHash(ctx, *b.numberOrHash) } } return b.header, err @@ -416,15 +389,12 @@ func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) { } func (b *Block) Number(ctx context.Context) (hexutil.Uint64, error) { - if b.num == nil || *b.num == rpc.LatestBlockNumber { - header, err := b.resolveHeader(ctx) - if err != nil { - return 0, err - } - num := rpc.BlockNumber(header.Number.Uint64()) - b.num = &num + header, err := b.resolveHeader(ctx) + if err != nil { + return 0, err } - return hexutil.Uint64(*b.num), nil + + return hexutil.Uint64(header.Number.Uint64()), nil } func (b *Block) Hash(ctx context.Context) (common.Hash, error) { @@ -456,26 +426,17 @@ func (b *Block) GasUsed(ctx context.Context) (hexutil.Uint64, error) { func (b *Block) Parent(ctx context.Context) (*Block, error) { // If the block header hasn't been fetched, and we'll need it, fetch it. - if b.num == nil && b.hash != (common.Hash{}) && b.header == nil { + if b.numberOrHash == nil && b.header == nil { if _, err := b.resolveHeader(ctx); err != nil { return nil, err } } if b.header != nil && b.header.Number.Uint64() > 0 { - num := rpc.BlockNumber(b.header.Number.Uint64() - 1) + num := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(b.header.Number.Uint64() - 1)) return &Block{ - backend: b.backend, - num: &num, - hash: b.header.ParentHash, - canonical: unknown, - }, nil - } - if b.num != nil && *b.num != 0 { - num := *b.num - 1 - return &Block{ - backend: b.backend, - num: &num, - canonical: isCanonical, + backend: b.backend, + numberOrHash: &num, + hash: b.header.ParentHash, }, nil } return nil, nil @@ -561,13 +522,11 @@ func (b *Block) Ommers(ctx context.Context) (*[]*Block, error) { } ret := make([]*Block, 0, len(block.Uncles())) for _, uncle := range block.Uncles() { - blockNumber := rpc.BlockNumber(uncle.Number.Uint64()) + blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) ret = append(ret, &Block{ - backend: b.backend, - num: &blockNumber, - hash: uncle.Hash(), - header: uncle, - canonical: notCanonical, + backend: b.backend, + numberOrHash: &blockNumberOrHash, + header: uncle, }) } return &ret, nil @@ -603,16 +562,26 @@ func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) { // BlockNumberArgs encapsulates arguments to accessors that specify a block number. type BlockNumberArgs struct { + // TODO: Ideally we could use input unions to allow the query to specify the + // block parameter by hash, block number, or tag but input unions aren't part of the + // standard GraphQL schema SDL yet, see: https://github.com/graphql/graphql-spec/issues/488 Block *hexutil.Uint64 } -// Number returns the provided block number, or rpc.LatestBlockNumber if none +// NumberOr returns the provided block number argument, or the "current" block number or hash if none // was provided. -func (a BlockNumberArgs) Number() rpc.BlockNumber { +func (a BlockNumberArgs) NumberOr(current rpc.BlockNumberOrHash) rpc.BlockNumberOrHash { if a.Block != nil { - return rpc.BlockNumber(*a.Block) + blockNr := rpc.BlockNumber(*a.Block) + return rpc.BlockNumberOrHashWithNumber(blockNr) } - return rpc.LatestBlockNumber + return current +} + +// NumberOrLatest returns the provided block number argument, or the "latest" block number if none +// was provided. +func (a BlockNumberArgs) NumberOrLatest() rpc.BlockNumberOrHash { + return a.NumberOr(rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)) } func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, error) { @@ -621,9 +590,9 @@ func (b *Block) Miner(ctx context.Context, args BlockNumberArgs) (*Account, erro return nil, err } return &Account{ - backend: b.backend, - address: header.Coinbase, - blockNumber: args.Number(), + backend: b.backend, + address: header.Coinbase, + blockNrOrHash: args.NumberOrLatest(), }, nil } @@ -683,13 +652,11 @@ func (b *Block) OmmerAt(ctx context.Context, args struct{ Index int32 }) (*Block return nil, nil } uncle := uncles[args.Index] - blockNumber := rpc.BlockNumber(uncle.Number.Uint64()) + blockNumberOrHash := rpc.BlockNumberOrHashWithHash(uncle.Hash(), false) return &Block{ - backend: b.backend, - num: &blockNumber, - hash: uncle.Hash(), - header: uncle, - canonical: notCanonical, + backend: b.backend, + numberOrHash: &blockNumberOrHash, + header: uncle, }, nil } @@ -757,20 +724,16 @@ func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteri func (b *Block) Account(ctx context.Context, args struct { Address common.Address }) (*Account, error) { - err := b.onMainChain(ctx) - if err != nil { - return nil, err - } - if b.num == nil { + if b.numberOrHash == nil { _, err := b.resolveHeader(ctx) if err != nil { return nil, err } } return &Account{ - backend: b.backend, - address: args.Address, - blockNumber: *b.num, + backend: b.backend, + address: args.Address, + blockNrOrHash: *b.numberOrHash, }, nil } @@ -807,17 +770,13 @@ func (c *CallResult) Status() hexutil.Uint64 { func (b *Block) Call(ctx context.Context, args struct { Data ethapi.CallArgs }) (*CallResult, error) { - err := b.onMainChain(ctx) - if err != nil { - return nil, err - } - if b.num == nil { - _, err := b.resolveHeader(ctx) + if b.numberOrHash == nil { + _, err := b.resolve(ctx) if err != nil { return nil, err } } - result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.num, nil, vm.Config{}, 5*time.Second, b.backend.RPCGasCap()) + result, gas, failed, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, vm.Config{}, 5*time.Second, b.backend.RPCGasCap()) status := hexutil.Uint64(1) if failed { status = 0 @@ -832,17 +791,13 @@ func (b *Block) Call(ctx context.Context, args struct { func (b *Block) EstimateGas(ctx context.Context, args struct { Data ethapi.CallArgs }) (hexutil.Uint64, error) { - err := b.onMainChain(ctx) - if err != nil { - return hexutil.Uint64(0), err - } - if b.num == nil { + if b.numberOrHash == nil { _, err := b.resolveHeader(ctx) if err != nil { return hexutil.Uint64(0), err } } - gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.num, b.backend.RPCGasCap()) + gas, err := ethapi.DoEstimateGas(ctx, b.backend, args.Data, *b.numberOrHash, b.backend.RPCGasCap()) return gas, err } @@ -875,17 +830,19 @@ func (p *Pending) Transactions(ctx context.Context) (*[]*Transaction, error) { func (p *Pending) Account(ctx context.Context, args struct { Address common.Address }) *Account { + pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) return &Account{ - backend: p.backend, - address: args.Address, - blockNumber: rpc.PendingBlockNumber, + backend: p.backend, + address: args.Address, + blockNrOrHash: pendingBlockNr, } } func (p *Pending) Call(ctx context.Context, args struct { Data ethapi.CallArgs }) (*CallResult, error) { - result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, rpc.PendingBlockNumber, nil, vm.Config{}, 5*time.Second, p.backend.RPCGasCap()) + pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + result, gas, failed, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, vm.Config{}, 5*time.Second, p.backend.RPCGasCap()) status := hexutil.Uint64(1) if failed { status = 0 @@ -900,7 +857,8 @@ func (p *Pending) Call(ctx context.Context, args struct { func (p *Pending) EstimateGas(ctx context.Context, args struct { Data ethapi.CallArgs }) (hexutil.Uint64, error) { - return ethapi.DoEstimateGas(ctx, p.backend, args.Data, rpc.PendingBlockNumber, p.backend.RPCGasCap()) + pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + return ethapi.DoEstimateGas(ctx, p.backend, args.Data, pendingBlockNr, p.backend.RPCGasCap()) } // Resolver is the top-level object in the GraphQL hierarchy. @@ -914,24 +872,23 @@ func (r *Resolver) Block(ctx context.Context, args struct { }) (*Block, error) { var block *Block if args.Number != nil { - num := rpc.BlockNumber(uint64(*args.Number)) + number := rpc.BlockNumber(uint64(*args.Number)) + numberOrHash := rpc.BlockNumberOrHashWithNumber(number) block = &Block{ - backend: r.backend, - num: &num, - canonical: isCanonical, + backend: r.backend, + numberOrHash: &numberOrHash, } } else if args.Hash != nil { + numberOrHash := rpc.BlockNumberOrHashWithHash(*args.Hash, false) block = &Block{ - backend: r.backend, - hash: *args.Hash, - canonical: unknown, + backend: r.backend, + numberOrHash: &numberOrHash, } } else { - num := rpc.LatestBlockNumber + numberOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) block = &Block{ - backend: r.backend, - num: &num, - canonical: isCanonical, + backend: r.backend, + numberOrHash: &numberOrHash, } } // Resolve the header, return nil if it doesn't exist. @@ -963,11 +920,10 @@ func (r *Resolver) Blocks(ctx context.Context, args struct { } ret := make([]*Block, 0, to-from+1) for i := from; i <= to; i++ { - num := i + numberOrHash := rpc.BlockNumberOrHashWithNumber(i) ret = append(ret, &Block{ - backend: r.backend, - num: &num, - canonical: isCanonical, + backend: r.backend, + numberOrHash: &numberOrHash, }) } return ret, nil diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go index 05204e5478..ea7bb7fc86 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/api.go @@ -427,7 +427,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c log.Warn("Failed data sign attempt", "address", addr, "err", err) return nil, err } - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper return signature, nil } @@ -442,13 +442,13 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c // // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { - if len(sig) != 65 { - return common.Address{}, fmt.Errorf("signature must be 65 bytes long") + if len(sig) != crypto.SignatureLength { + return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) } - if sig[64] != 27 && sig[64] != 28 { + if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 { return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") } - sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 + sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1 rpk, err := crypto.SigToPub(accounts.TextHash(data), sig) if err != nil { @@ -530,8 +530,8 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 { // GetBalance returns the amount of wei for the given address in the state of the // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. -func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Big, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) +func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -555,8 +555,8 @@ type StorageResult struct { } // GetProof returns the Merkle-proof for a given account and optionally some storage keys. -func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNr rpc.BlockNumber) (*AccountResult, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) +func (s *PublicBlockChainAPI) GetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) { + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -712,8 +712,8 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, bloc } // GetCode returns the code stored at the given address in the state for the given block number. -func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) +func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -724,8 +724,8 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Addres // GetStorageAt returns the storage from the state at the given address, key and // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. -func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) +func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error) { + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -757,10 +757,10 @@ type account struct { StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` } -func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { +func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now()) - state, header, err := b.StateAndHeaderByNumber(ctx, blockNr) + state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, 0, false, err } @@ -874,16 +874,16 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb // // Note, this function doesn't make and changes in the state/blockchain and is // useful to execute and retrieve values. -func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, overrides *map[common.Address]account) (hexutil.Bytes, error) { +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]account) (hexutil.Bytes, error) { var accounts map[common.Address]account if overrides != nil { accounts = *overrides } - result, _, _, err := DoCall(ctx, s.b, args, blockNr, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap()) + result, _, _, err := DoCall(ctx, s.b, args, blockNrOrHash, accounts, vm.Config{}, 5*time.Second, s.b.RPCGasCap()) return (hexutil.Bytes)(result), err } -func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, gasCap *big.Int) (hexutil.Uint64, error) { +func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (hexutil.Uint64, error) { // Binary search the gas requirement, as it may be higher than the amount used var ( lo uint64 = params.TxGas - 1 @@ -894,7 +894,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl hi = uint64(*args.Gas) } else { // Retrieve the block to act as the gas ceiling - block, err := b.BlockByNumber(ctx, blockNr) + block, err := b.BlockByNumberOrHash(ctx, blockNrOrHash) if err != nil { return 0, err } @@ -910,7 +910,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl executable := func(gas uint64) bool { args.Gas = (*hexutil.Uint64)(&gas) - _, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, nil, vm.Config{}, 0, gasCap) + _, _, failed, err := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap) if err != nil || failed { return false } @@ -937,7 +937,8 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.Bl // EstimateGas returns an estimate of the amount of gas needed to execute the // given transaction against the current pending block. func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) { - return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber, s.b.RPCGasCap()) + blockNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + return DoEstimateGas(ctx, s.b, args, blockNrOrHash, s.b.RPCGasCap()) } // ExecutionResult groups all structured logs emitted by the EVM @@ -1224,9 +1225,9 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont } // GetTransactionCount returns the number of transactions the given address has sent for the given block number -func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) { +func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) { // Ask transaction pool for the nonce which includes pending transactions - if blockNr == rpc.PendingBlockNumber { + if blockNr, ok := blockNrOrHash.Number(); ok && blockNr == rpc.PendingBlockNumber { nonce, err := s.b.GetPoolNonce(ctx, address) if err != nil { return nil, err @@ -1234,7 +1235,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr return (*hexutil.Uint64)(&nonce), nil } // Resolve block number and use its state to ask for the nonce - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + state, _, err := s.b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) if state == nil || err != nil { return nil, err } @@ -1405,7 +1406,8 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { Value: args.Value, Data: input, } - estimated, err := DoEstimateGas(ctx, b, callArgs, rpc.PendingBlockNumber, b.RPCGasCap()) + pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, b.RPCGasCap()) if err != nil { return err } @@ -1479,6 +1481,22 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen return SubmitTransaction(ctx, s.b, signed) } +// FillTransaction fills the defaults (nonce, gas, gasPrice) on a given unsigned transaction, +// and returns it to the caller for further processing (signing + broadcast) +func (s *PublicTransactionPoolAPI) FillTransaction(ctx context.Context, args SendTxArgs) (*SignTransactionResult, error) { + // Set some sanity defaults and terminate on failure + if err := args.setDefaults(ctx, s.b); err != nil { + return nil, err + } + // Assemble the transaction and obtain rlp + tx := args.toTransaction() + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return nil, err + } + return &SignTransactionResult{data, tx}, nil +} + // SendRawTransaction will add the signed transaction to the transaction pool. // The sender is responsible for signing the transaction and using the correct nonce. func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) { diff --git a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go index 06c6db33b1..73b6c89cea 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/ethapi/backend.go @@ -52,9 +52,12 @@ type Backend interface { SetHead(number uint64) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) + StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetTd(hash common.Hash) *big.Int GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) diff --git a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go index 4a5d4b4ddb..86e5754392 100644 --- a/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go +++ b/vendor/github.com/ethereum/go-ethereum/internal/web3ext/web3ext.go @@ -483,6 +483,12 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.inputTransactionFormatter] }), + new web3._extend.Method({ + name: 'fillTransaction', + call: 'eth_fillTransaction', + params: 1, + inputFormatter: [web3._extend.formatters.inputTransactionFormatter] + }), new web3._extend.Method({ name: 'getHeaderByNumber', call: 'eth_getHeaderByNumber', diff --git a/vendor/github.com/ethereum/go-ethereum/les/api.go b/vendor/github.com/ethereum/go-ethereum/les/api.go index e20f72cad0..bbef771f04 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/api.go +++ b/vendor/github.com/ethereum/go-ethereum/les/api.go @@ -30,15 +30,11 @@ var ( // PrivateLightAPI provides an API to access the LES light server or light client. type PrivateLightAPI struct { backend *lesCommons - reg *checkpointOracle } // NewPrivateLightAPI creates a new LES service API. -func NewPrivateLightAPI(backend *lesCommons, reg *checkpointOracle) *PrivateLightAPI { - return &PrivateLightAPI{ - backend: backend, - reg: reg, - } +func NewPrivateLightAPI(backend *lesCommons) *PrivateLightAPI { + return &PrivateLightAPI{backend: backend} } // LatestCheckpoint returns the latest local checkpoint package. @@ -67,7 +63,7 @@ func (api *PrivateLightAPI) LatestCheckpoint() ([4]string, error) { // result[2], 32 bytes hex encoded latest section bloom trie root hash func (api *PrivateLightAPI) GetCheckpoint(index uint64) ([3]string, error) { var res [3]string - cp := api.backend.getLocalCheckpoint(index) + cp := api.backend.localCheckpoint(index) if cp.Empty() { return res, errNoCheckpoint } @@ -77,8 +73,8 @@ func (api *PrivateLightAPI) GetCheckpoint(index uint64) ([3]string, error) { // GetCheckpointContractAddress returns the contract contract address in hex format. func (api *PrivateLightAPI) GetCheckpointContractAddress() (string, error) { - if api.reg == nil { + if api.backend.oracle == nil { return "", errNotActivated } - return api.reg.config.Address.Hex(), nil + return api.backend.oracle.config.Address.Hex(), nil } diff --git a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go index 07601c2423..e01e1be98b 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/api_backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/api_backend.go @@ -54,7 +54,7 @@ func (b *LesApiBackend) CurrentBlock() *types.Block { } func (b *LesApiBackend) SetHead(number uint64) { - b.eth.protocolManager.downloader.Cancel() + b.eth.handler.downloader.Cancel() b.eth.blockchain.SetHead(number) } @@ -65,6 +65,26 @@ func (b *LesApiBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(number)) } +func (b *LesApiBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return b.HeaderByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header, err := b.HeaderByHash(ctx, hash) + if err != nil { + return nil, err + } + if header == nil { + return nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { + return nil, errors.New("hash is not currently canonical") + } + return header, nil + } + return nil, errors.New("invalid arguments; neither block nor hash specified") +} + func (b *LesApiBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) { return b.eth.blockchain.GetHeaderByHash(hash), nil } @@ -81,6 +101,26 @@ func (b *LesApiBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ return b.eth.blockchain.GetBlockByHash(ctx, hash) } +func (b *LesApiBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return b.BlockByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + block, err := b.BlockByHash(ctx, hash) + if err != nil { + return nil, err + } + if block == nil { + return nil, errors.New("header found, but block body is missing") + } + if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(block.NumberU64()) != hash { + return nil, errors.New("hash is not currently canonical") + } + return block, nil + } + return nil, errors.New("invalid arguments; neither block nor hash specified") +} + func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) { header, err := b.HeaderByNumber(ctx, number) if err != nil { @@ -92,6 +132,23 @@ func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B return light.NewState(ctx, header, b.eth.odr), header, nil } +func (b *LesApiBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) { + if blockNr, ok := blockNrOrHash.Number(); ok { + return b.StateAndHeaderByNumber(ctx, blockNr) + } + if hash, ok := blockNrOrHash.Hash(); ok { + header := b.eth.blockchain.GetHeaderByHash(hash) + if header == nil { + return nil, nil, errors.New("header for hash not found") + } + if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash { + return nil, nil, errors.New("hash is not currently canonical") + } + return light.NewState(ctx, header, b.eth.odr), header, nil + } + return nil, nil, errors.New("invalid arguments; neither block nor hash specified") +} + func (b *LesApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil { return light.GetBlockReceipts(ctx, b.eth.odr, hash, *number) diff --git a/vendor/github.com/ethereum/go-ethereum/les/balance.go b/vendor/github.com/ethereum/go-ethereum/les/balance.go index 4f08a304eb..2813db01c5 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/balance.go +++ b/vendor/github.com/ethereum/go-ethereum/les/balance.go @@ -42,7 +42,7 @@ type balanceTracker struct { negTimeFactor, negRequestFactor float64 sumReqCost uint64 lastUpdate, nextUpdate, initTime mclock.AbsTime - updateEvent mclock.Event + updateEvent mclock.Timer // since only a limited and fixed number of callbacks are needed, they are // stored in a fixed size array ordered by priority threshold. callbacks [balanceCallbackCount]balanceCallback @@ -67,7 +67,7 @@ type balanceCallback struct { // init initializes balanceTracker func (bt *balanceTracker) init(clock mclock.Clock, capacity uint64) { bt.clock = clock - bt.initTime = clock.Now() + bt.initTime, bt.lastUpdate = clock.Now(), clock.Now() // Init timestamps for i := range bt.callbackIndex { bt.callbackIndex[i] = -1 } @@ -86,7 +86,7 @@ func (bt *balanceTracker) stop(now mclock.AbsTime) { bt.timeFactor = 0 bt.requestFactor = 0 if bt.updateEvent != nil { - bt.updateEvent.Cancel() + bt.updateEvent.Stop() bt.updateEvent = nil } } @@ -235,7 +235,7 @@ func (bt *balanceTracker) checkCallbacks(now mclock.AbsTime) { // updateAfter schedules a balance update and callback check in the future func (bt *balanceTracker) updateAfter(dt time.Duration) { - if bt.updateEvent == nil || bt.updateEvent.Cancel() { + if bt.updateEvent == nil || bt.updateEvent.Stop() { if dt == 0 { bt.updateEvent = nil } else { diff --git a/vendor/github.com/ethereum/go-ethereum/les/benchmark.go b/vendor/github.com/ethereum/go-ethereum/les/benchmark.go index 74dfcf7c9e..42eeef10f3 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/benchmark.go +++ b/vendor/github.com/ethereum/go-ethereum/les/benchmark.go @@ -21,6 +21,7 @@ import ( "fmt" "math/big" "math/rand" + "sync" "time" "github.com/ethereum/go-ethereum/common" @@ -39,7 +40,7 @@ import ( // requestBenchmark is an interface for different randomized request generators type requestBenchmark interface { // init initializes the generator for generating the given number of randomized requests - init(pm *ProtocolManager, count int) error + init(h *serverHandler, count int) error // request initiates sending a single request to the given peer request(peer *peer, index int) error } @@ -52,10 +53,10 @@ type benchmarkBlockHeaders struct { hashes []common.Hash } -func (b *benchmarkBlockHeaders) init(pm *ProtocolManager, count int) error { +func (b *benchmarkBlockHeaders) init(h *serverHandler, count int) error { d := int64(b.amount-1) * int64(b.skip+1) b.offset = 0 - b.randMax = pm.blockchain.CurrentHeader().Number.Int64() + 1 - d + b.randMax = h.blockchain.CurrentHeader().Number.Int64() + 1 - d if b.randMax < 0 { return fmt.Errorf("chain is too short") } @@ -65,7 +66,7 @@ func (b *benchmarkBlockHeaders) init(pm *ProtocolManager, count int) error { if b.byHash { b.hashes = make([]common.Hash, count) for i := range b.hashes { - b.hashes[i] = rawdb.ReadCanonicalHash(pm.chainDb, uint64(b.offset+rand.Int63n(b.randMax))) + b.hashes[i] = rawdb.ReadCanonicalHash(h.chainDb, uint64(b.offset+rand.Int63n(b.randMax))) } } return nil @@ -85,11 +86,11 @@ type benchmarkBodiesOrReceipts struct { hashes []common.Hash } -func (b *benchmarkBodiesOrReceipts) init(pm *ProtocolManager, count int) error { - randMax := pm.blockchain.CurrentHeader().Number.Int64() + 1 +func (b *benchmarkBodiesOrReceipts) init(h *serverHandler, count int) error { + randMax := h.blockchain.CurrentHeader().Number.Int64() + 1 b.hashes = make([]common.Hash, count) for i := range b.hashes { - b.hashes[i] = rawdb.ReadCanonicalHash(pm.chainDb, uint64(rand.Int63n(randMax))) + b.hashes[i] = rawdb.ReadCanonicalHash(h.chainDb, uint64(rand.Int63n(randMax))) } return nil } @@ -108,8 +109,8 @@ type benchmarkProofsOrCode struct { headHash common.Hash } -func (b *benchmarkProofsOrCode) init(pm *ProtocolManager, count int) error { - b.headHash = pm.blockchain.CurrentHeader().Hash() +func (b *benchmarkProofsOrCode) init(h *serverHandler, count int) error { + b.headHash = h.blockchain.CurrentHeader().Hash() return nil } @@ -130,11 +131,11 @@ type benchmarkHelperTrie struct { sectionCount, headNum uint64 } -func (b *benchmarkHelperTrie) init(pm *ProtocolManager, count int) error { +func (b *benchmarkHelperTrie) init(h *serverHandler, count int) error { if b.bloom { - b.sectionCount, b.headNum, _ = pm.server.bloomTrieIndexer.Sections() + b.sectionCount, b.headNum, _ = h.server.bloomTrieIndexer.Sections() } else { - b.sectionCount, _, _ = pm.server.chtIndexer.Sections() + b.sectionCount, _, _ = h.server.chtIndexer.Sections() b.headNum = b.sectionCount*params.CHTFrequency - 1 } if b.sectionCount == 0 { @@ -170,7 +171,7 @@ type benchmarkTxSend struct { txs types.Transactions } -func (b *benchmarkTxSend) init(pm *ProtocolManager, count int) error { +func (b *benchmarkTxSend) init(h *serverHandler, count int) error { key, _ := crypto.GenerateKey() addr := crypto.PubkeyToAddress(key.PublicKey) signer := types.NewEIP155Signer(big.NewInt(18)) @@ -196,7 +197,7 @@ func (b *benchmarkTxSend) request(peer *peer, index int) error { // benchmarkTxStatus implements requestBenchmark type benchmarkTxStatus struct{} -func (b *benchmarkTxStatus) init(pm *ProtocolManager, count int) error { +func (b *benchmarkTxStatus) init(h *serverHandler, count int) error { return nil } @@ -217,7 +218,7 @@ type benchmarkSetup struct { // runBenchmark runs a benchmark cycle for all benchmark types in the specified // number of passes -func (pm *ProtocolManager) runBenchmark(benchmarks []requestBenchmark, passCount int, targetTime time.Duration) []*benchmarkSetup { +func (h *serverHandler) runBenchmark(benchmarks []requestBenchmark, passCount int, targetTime time.Duration) []*benchmarkSetup { setup := make([]*benchmarkSetup, len(benchmarks)) for i, b := range benchmarks { setup[i] = &benchmarkSetup{req: b} @@ -239,7 +240,7 @@ func (pm *ProtocolManager) runBenchmark(benchmarks []requestBenchmark, passCount if next.totalTime > 0 { count = int(uint64(next.totalCount) * uint64(targetTime) / uint64(next.totalTime)) } - if err := pm.measure(next, count); err != nil { + if err := h.measure(next, count); err != nil { next.err = err } } @@ -275,14 +276,15 @@ func (m *meteredPipe) WriteMsg(msg p2p.Msg) error { // measure runs a benchmark for a single type in a single pass, with the given // number of requests -func (pm *ProtocolManager) measure(setup *benchmarkSetup, count int) error { +func (h *serverHandler) measure(setup *benchmarkSetup, count int) error { clientPipe, serverPipe := p2p.MsgPipe() clientMeteredPipe := &meteredPipe{rw: clientPipe} serverMeteredPipe := &meteredPipe{rw: serverPipe} var id enode.ID rand.Read(id[:]) - clientPeer := pm.newPeer(lpv2, NetworkId, p2p.NewPeer(id, "client", nil), clientMeteredPipe) - serverPeer := pm.newPeer(lpv2, NetworkId, p2p.NewPeer(id, "server", nil), serverMeteredPipe) + + clientPeer := newPeer(lpv2, NetworkId, false, p2p.NewPeer(id, "client", nil), clientMeteredPipe) + serverPeer := newPeer(lpv2, NetworkId, false, p2p.NewPeer(id, "server", nil), serverMeteredPipe) serverPeer.sendQueue = newExecQueue(count) serverPeer.announceType = announceTypeNone serverPeer.fcCosts = make(requestCostTable) @@ -291,10 +293,10 @@ func (pm *ProtocolManager) measure(setup *benchmarkSetup, count int) error { serverPeer.fcCosts[code] = c } serverPeer.fcParams = flowcontrol.ServerParams{BufLimit: 1, MinRecharge: 1} - serverPeer.fcClient = flowcontrol.NewClientNode(pm.server.fcManager, serverPeer.fcParams) + serverPeer.fcClient = flowcontrol.NewClientNode(h.server.fcManager, serverPeer.fcParams) defer serverPeer.fcClient.Disconnect() - if err := setup.req.init(pm, count); err != nil { + if err := setup.req.init(h, count); err != nil { return err } @@ -311,7 +313,7 @@ func (pm *ProtocolManager) measure(setup *benchmarkSetup, count int) error { }() go func() { for i := 0; i < count; i++ { - if err := pm.handleMsg(serverPeer); err != nil { + if err := h.handleMsg(serverPeer, &sync.WaitGroup{}); err != nil { errCh <- err return } @@ -336,7 +338,7 @@ func (pm *ProtocolManager) measure(setup *benchmarkSetup, count int) error { if err != nil { return err } - case <-pm.quitSync: + case <-h.closeCh: clientPipe.Close() serverPipe.Close() return fmt.Errorf("Benchmark cancelled") diff --git a/vendor/github.com/ethereum/go-ethereum/les/bloombits.go b/vendor/github.com/ethereum/go-ethereum/les/bloombits.go index aea0fcd5f4..a98524ce2e 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/bloombits.go +++ b/vendor/github.com/ethereum/go-ethereum/les/bloombits.go @@ -46,9 +46,10 @@ const ( func (eth *LightEthereum) startBloomHandlers(sectionSize uint64) { for i := 0; i < bloomServiceThreads; i++ { go func() { + defer eth.wg.Done() for { select { - case <-eth.shutdownChan: + case <-eth.closeCh: return case request := <-eth.bloomRequests: diff --git a/vendor/github.com/ethereum/go-ethereum/les/checkpointoracle.go b/vendor/github.com/ethereum/go-ethereum/les/checkpointoracle.go index 4695fbc16c..5494e3d6d9 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/checkpointoracle.go +++ b/vendor/github.com/ethereum/go-ethereum/les/checkpointoracle.go @@ -35,11 +35,8 @@ type checkpointOracle struct { config *params.CheckpointOracleConfig contract *checkpointoracle.CheckpointOracle - // Whether the contract backend is set. - running int32 - - getLocal func(uint64) params.TrustedCheckpoint // Function used to retrieve local checkpoint - syncDoneHook func() // Function used to notify that light syncing has completed. + running int32 // Flag whether the contract backend is set or not + getLocal func(uint64) params.TrustedCheckpoint // Function used to retrieve local checkpoint } // newCheckpointOracle returns a checkpoint registrar handler. diff --git a/vendor/github.com/ethereum/go-ethereum/les/backend.go b/vendor/github.com/ethereum/go-ethereum/les/client.go similarity index 78% rename from vendor/github.com/ethereum/go-ethereum/les/backend.go rename to vendor/github.com/ethereum/go-ethereum/les/client.go index c067afaea6..b367681f37 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/backend.go +++ b/vendor/github.com/ethereum/go-ethereum/les/client.go @@ -19,8 +19,6 @@ package les import ( "fmt" - "sync" - "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -42,7 +40,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" + "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -50,33 +48,23 @@ import ( type LightEthereum struct { lesCommons - odr *LesOdr - chainConfig *params.ChainConfig - // Channel for shutting down the service - shutdownChan chan bool - - // Handlers - peers *peerSet - txPool *light.TxPool - blockchain *light.LightChain - serverPool *serverPool reqDist *requestDistributor retriever *retrieveManager + odr *LesOdr relay *lesTxRelay + handler *clientHandler + txPool *light.TxPool + blockchain *light.LightChain + serverPool *serverPool bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests - bloomIndexer *core.ChainIndexer - - ApiBackend *LesApiBackend + bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports + ApiBackend *LesApiBackend eventMux *event.TypeMux engine consensus.Engine accountManager *accounts.Manager - - networkId uint64 - netRPCService *ethapi.PublicNetAPI - - wg sync.WaitGroup + netRPCService *ethapi.PublicNetAPI } func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { @@ -84,33 +72,31 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideIstanbul) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { return nil, genesisErr } log.Info("Initialised chain configuration", "config", chainConfig) peers := newPeerSet() - quitSync := make(chan struct{}) - leth := &LightEthereum{ lesCommons: lesCommons{ - chainDb: chainDb, - config: config, - iConfig: light.DefaultClientIndexerConfig, + genesis: genesisHash, + config: config, + chainConfig: chainConfig, + iConfig: light.DefaultClientIndexerConfig, + chainDb: chainDb, + peers: peers, + closeCh: make(chan struct{}), }, - chainConfig: chainConfig, eventMux: ctx.EventMux, - peers: peers, - reqDist: newRequestDistributor(peers, quitSync, &mclock.System{}), + reqDist: newRequestDistributor(peers, &mclock.System{}), accountManager: ctx.AccountManager, engine: eth.CreateConsensusEngine(ctx, chainConfig, &config.Ethash, nil, false, chainDb), - shutdownChan: make(chan bool), - networkId: config.NetworkId, bloomRequests: make(chan chan *bloombits.Retrieval), bloomIndexer: eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), + serverPool: newServerPool(chainDb, config.UltraLightServers), } - leth.serverPool = newServerPool(chainDb, quitSync, &leth.wg, leth.config.UltraLightServers) leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool) leth.relay = newLesTxRelay(peers, leth.retriever) @@ -128,11 +114,26 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil { return nil, err } + leth.chainReader = leth.blockchain + leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay) + + // Set up checkpoint oracle. + oracle := config.CheckpointOracle + if oracle == nil { + oracle = params.CheckpointOracles[genesisHash] + } + leth.oracle = newCheckpointOracle(oracle, leth.localCheckpoint) + // Note: AddChildIndexer starts the update process for the child leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer) leth.chtIndexer.Start(leth.blockchain) leth.bloomIndexer.Start(leth.blockchain) + leth.handler = newClientHandler(config.UltraLightServers, config.UltraLightFraction, checkpoint, leth) + if leth.handler.ulc != nil { + log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.handler.ulc.keys), "minTrustedFraction", leth.handler.ulc.fraction) + leth.blockchain.DisableCheckFreq() + } // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) @@ -140,41 +141,16 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) } - leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay) leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil} - gpoParams := config.GPO if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice } leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) - oracle := config.CheckpointOracle - if oracle == nil { - oracle = params.CheckpointOracles[genesisHash] - } - registrar := newCheckpointOracle(oracle, leth.getLocalCheckpoint) - if leth.protocolManager, err = NewProtocolManager(leth.chainConfig, checkpoint, light.DefaultClientIndexerConfig, config.UltraLightServers, config.UltraLightFraction, true, config.NetworkId, leth.eventMux, leth.peers, leth.blockchain, nil, chainDb, leth.odr, leth.serverPool, registrar, quitSync, &leth.wg, nil); err != nil { - return nil, err - } - if leth.protocolManager.ulc != nil { - log.Warn("Ultra light client is enabled", "servers", len(config.UltraLightServers), "fraction", config.UltraLightFraction) - leth.blockchain.DisableCheckFreq() - } return leth, nil } -func lesTopic(genesisHash common.Hash, protocolVersion uint) discv5.Topic { - var name string - switch protocolVersion { - case lpv2: - name = "LES2" - default: - panic(nil) - } - return discv5.Topic(name + "@" + common.Bytes2Hex(genesisHash.Bytes()[0:8])) -} - type LightDummyAPI struct{} // Etherbase is the address that mining rewards will be send to @@ -209,7 +185,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(s.handler.downloader, s.eventMux), Public: true, }, { Namespace: "eth", @@ -224,7 +200,7 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "les", Version: "1.0", - Service: NewPrivateLightAPI(&s.lesCommons, s.protocolManager.reg), + Service: NewPrivateLightAPI(&s.lesCommons), Public: false, }, }...) @@ -238,54 +214,63 @@ func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchai func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } func (s *LightEthereum) Engine() consensus.Engine { return s.engine } func (s *LightEthereum) LesVersion() int { return int(ClientProtocolVersions[0]) } -func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } +func (s *LightEthereum) Downloader() *downloader.Downloader { return s.handler.downloader } func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } // Protocols implements node.Service, returning all the currently configured // network protocols to start. func (s *LightEthereum) Protocols() []p2p.Protocol { - return s.makeProtocols(ClientProtocolVersions) + return s.makeProtocols(ClientProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { + if p := s.peers.Peer(peerIdToString(id)); p != nil { + return p.Info() + } + return nil + }) } // Start implements node.Service, starting all internal goroutines needed by the -// Ethereum protocol implementation. +// light ethereum protocol implementation. func (s *LightEthereum) Start(srvr *p2p.Server) error { log.Warn("Light client mode is an experimental feature") + + // Start bloom request workers. + s.wg.Add(bloomServiceThreads) s.startBloomHandlers(params.BloomBitsBlocksClient) - s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId) + + s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.config.NetworkId) + // clients are searching for the first advertised protocol in the list protocolVersion := AdvertiseProtocolVersions[0] s.serverPool.start(srvr, lesTopic(s.blockchain.Genesis().Hash(), protocolVersion)) - s.protocolManager.Start(s.config.LightPeers) return nil } // Stop implements node.Service, terminating all internal goroutines used by the // Ethereum protocol. func (s *LightEthereum) Stop() error { + close(s.closeCh) + s.peers.Close() + s.reqDist.close() s.odr.Stop() s.relay.Stop() s.bloomIndexer.Close() s.chtIndexer.Close() s.blockchain.Stop() - s.protocolManager.Stop() + s.handler.stop() s.txPool.Stop() s.engine.Close() - s.eventMux.Stop() - - time.Sleep(time.Millisecond * 200) + s.serverPool.stop() s.chainDb.Close() - close(s.shutdownChan) - + s.wg.Wait() + log.Info("Light ethereum stopped") return nil } // SetClient sets the rpc client and binds the registrar contract. func (s *LightEthereum) SetContractBackend(backend bind.ContractBackend) { - // Short circuit if registrar is nil - if s.protocolManager.reg == nil { + if s.oracle == nil { return } - s.protocolManager.reg.start(backend) + s.oracle.start(backend) } diff --git a/vendor/github.com/ethereum/go-ethereum/les/client_handler.go b/vendor/github.com/ethereum/go-ethereum/les/client_handler.go new file mode 100644 index 0000000000..7fdb165719 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/client_handler.go @@ -0,0 +1,403 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" +) + +// clientHandler is responsible for receiving and processing all incoming server +// responses. +type clientHandler struct { + ulc *ulc + checkpoint *params.TrustedCheckpoint + fetcher *lightFetcher + downloader *downloader.Downloader + backend *LightEthereum + + closeCh chan struct{} + wg sync.WaitGroup // WaitGroup used to track all connected peers. + syncDone func() // Test hooks when syncing is done. +} + +func newClientHandler(ulcServers []string, ulcFraction int, checkpoint *params.TrustedCheckpoint, backend *LightEthereum) *clientHandler { + handler := &clientHandler{ + checkpoint: checkpoint, + backend: backend, + closeCh: make(chan struct{}), + } + if ulcServers != nil { + ulc, err := newULC(ulcServers, ulcFraction) + if err != nil { + log.Error("Failed to initialize ultra light client") + } + handler.ulc = ulc + log.Info("Enable ultra light client mode") + } + var height uint64 + if checkpoint != nil { + height = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1 + } + handler.fetcher = newLightFetcher(handler) + handler.downloader = downloader.New(height, backend.chainDb, nil, backend.eventMux, nil, backend.blockchain, handler.removePeer) + handler.backend.peers.notify((*downloaderPeerNotify)(handler)) + return handler +} + +func (h *clientHandler) stop() { + close(h.closeCh) + h.downloader.Terminate() + h.fetcher.close() + h.wg.Wait() +} + +// runPeer is the p2p protocol run function for the given version. +func (h *clientHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error { + trusted := false + if h.ulc != nil { + trusted = h.ulc.trusted(p.ID()) + } + peer := newPeer(int(version), h.backend.config.NetworkId, trusted, p, newMeteredMsgWriter(rw, int(version))) + peer.poolEntry = h.backend.serverPool.connect(peer, peer.Node()) + if peer.poolEntry == nil { + return p2p.DiscRequested + } + h.wg.Add(1) + defer h.wg.Done() + err := h.handle(peer) + h.backend.serverPool.disconnect(peer.poolEntry) + return err +} + +func (h *clientHandler) handle(p *peer) error { + if h.backend.peers.Len() >= h.backend.config.LightPeers && !p.Peer.Info().Network.Trusted { + return p2p.DiscTooManyPeers + } + p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) + + // Execute the LES handshake + var ( + head = h.backend.blockchain.CurrentHeader() + hash = head.Hash() + number = head.Number.Uint64() + td = h.backend.blockchain.GetTd(hash, number) + ) + if err := p.Handshake(td, hash, number, h.backend.blockchain.Genesis().Hash(), nil); err != nil { + p.Log().Debug("Light Ethereum handshake failed", "err", err) + return err + } + // Register the peer locally + if err := h.backend.peers.Register(p); err != nil { + p.Log().Error("Light Ethereum peer registration failed", "err", err) + return err + } + serverConnectionGauge.Update(int64(h.backend.peers.Len())) + + connectedAt := mclock.Now() + defer func() { + h.backend.peers.Unregister(p.id) + connectionTimer.Update(time.Duration(mclock.Now() - connectedAt)) + serverConnectionGauge.Update(int64(h.backend.peers.Len())) + }() + + h.fetcher.announce(p, p.headInfo) + + // pool entry can be nil during the unit test. + if p.poolEntry != nil { + h.backend.serverPool.registered(p.poolEntry) + } + // Spawn a main loop to handle all incoming messages. + for { + if err := h.handleMsg(p); err != nil { + p.Log().Debug("Light Ethereum message handling failed", "err", err) + p.fcServer.DumpLogs() + return err + } + } +} + +// handleMsg is invoked whenever an inbound message is received from a remote +// peer. The remote connection is torn down upon returning any error. +func (h *clientHandler) handleMsg(p *peer) error { + // Read the next message from the remote peer, and ensure it's fully consumed + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size) + + if msg.Size > ProtocolMaxMsgSize { + return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + defer msg.Discard() + + var deliverMsg *Msg + + // Handle the message depending on its contents + switch msg.Code { + case AnnounceMsg: + p.Log().Trace("Received announce message") + var req announceData + if err := msg.Decode(&req); err != nil { + return errResp(ErrDecode, "%v: %v", msg, err) + } + if err := req.sanityCheck(); err != nil { + return err + } + update, size := req.Update.decode() + if p.rejectUpdate(size) { + return errResp(ErrRequestRejected, "") + } + p.updateFlowControl(update) + + if req.Hash != (common.Hash{}) { + if p.announceType == announceTypeNone { + return errResp(ErrUnexpectedResponse, "") + } + if p.announceType == announceTypeSigned { + if err := req.checkSignature(p.ID(), update); err != nil { + p.Log().Trace("Invalid announcement signature", "err", err) + return err + } + p.Log().Trace("Valid announcement signature") + } + p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth) + h.fetcher.announce(p, &req) + } + case BlockHeadersMsg: + p.Log().Trace("Received block header response message") + var resp struct { + ReqID, BV uint64 + Headers []*types.Header + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + if h.fetcher.requestedID(resp.ReqID) { + h.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) + } else { + if err := h.downloader.DeliverHeaders(p.id, resp.Headers); err != nil { + log.Debug("Failed to deliver headers", "err", err) + } + } + case BlockBodiesMsg: + p.Log().Trace("Received block bodies response") + var resp struct { + ReqID, BV uint64 + Data []*types.Body + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgBlockBodies, + ReqID: resp.ReqID, + Obj: resp.Data, + } + case CodeMsg: + p.Log().Trace("Received code response") + var resp struct { + ReqID, BV uint64 + Data [][]byte + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgCode, + ReqID: resp.ReqID, + Obj: resp.Data, + } + case ReceiptsMsg: + p.Log().Trace("Received receipts response") + var resp struct { + ReqID, BV uint64 + Receipts []types.Receipts + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgReceipts, + ReqID: resp.ReqID, + Obj: resp.Receipts, + } + case ProofsV2Msg: + p.Log().Trace("Received les/2 proofs response") + var resp struct { + ReqID, BV uint64 + Data light.NodeList + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgProofsV2, + ReqID: resp.ReqID, + Obj: resp.Data, + } + case HelperTrieProofsMsg: + p.Log().Trace("Received helper trie proof response") + var resp struct { + ReqID, BV uint64 + Data HelperTrieResps + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgHelperTrieProofs, + ReqID: resp.ReqID, + Obj: resp.Data, + } + case TxStatusMsg: + p.Log().Trace("Received tx status response") + var resp struct { + ReqID, BV uint64 + Status []light.TxStatus + } + if err := msg.Decode(&resp); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ReceivedReply(resp.ReqID, resp.BV) + deliverMsg = &Msg{ + MsgType: MsgTxStatus, + ReqID: resp.ReqID, + Obj: resp.Status, + } + case StopMsg: + p.freezeServer(true) + h.backend.retriever.frozen(p) + p.Log().Debug("Service stopped") + case ResumeMsg: + var bv uint64 + if err := msg.Decode(&bv); err != nil { + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + p.fcServer.ResumeFreeze(bv) + p.freezeServer(false) + p.Log().Debug("Service resumed") + default: + p.Log().Trace("Received invalid message", "code", msg.Code) + return errResp(ErrInvalidMsgCode, "%v", msg.Code) + } + // Deliver the received response to retriever. + if deliverMsg != nil { + if err := h.backend.retriever.deliver(p, deliverMsg); err != nil { + p.responseErrors++ + if p.responseErrors > maxResponseErrors { + return err + } + } + } + return nil +} + +func (h *clientHandler) removePeer(id string) { + h.backend.peers.Unregister(id) +} + +type peerConnection struct { + handler *clientHandler + peer *peer +} + +func (pc *peerConnection) Head() (common.Hash, *big.Int) { + return pc.peer.HeadAndTd() +} + +func (pc *peerConnection) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + peer := dp.(*peer) + return peer.GetRequestCost(GetBlockHeadersMsg, amount) + }, + canSend: func(dp distPeer) bool { + return dp.(*peer) == pc.peer + }, + request: func(dp distPeer) func() { + reqID := genReqID() + peer := dp.(*peer) + cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) + peer.fcServer.QueuedRequest(reqID, cost) + return func() { peer.RequestHeadersByHash(reqID, cost, origin, amount, skip, reverse) } + }, + } + _, ok := <-pc.handler.backend.reqDist.queue(rq) + if !ok { + return light.ErrNoPeers + } + return nil +} + +func (pc *peerConnection) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { + rq := &distReq{ + getCost: func(dp distPeer) uint64 { + peer := dp.(*peer) + return peer.GetRequestCost(GetBlockHeadersMsg, amount) + }, + canSend: func(dp distPeer) bool { + return dp.(*peer) == pc.peer + }, + request: func(dp distPeer) func() { + reqID := genReqID() + peer := dp.(*peer) + cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) + peer.fcServer.QueuedRequest(reqID, cost) + return func() { peer.RequestHeadersByNumber(reqID, cost, origin, amount, skip, reverse) } + }, + } + _, ok := <-pc.handler.backend.reqDist.queue(rq) + if !ok { + return light.ErrNoPeers + } + return nil +} + +// downloaderPeerNotify implements peerSetNotify +type downloaderPeerNotify clientHandler + +func (d *downloaderPeerNotify) registerPeer(p *peer) { + h := (*clientHandler)(d) + pc := &peerConnection{ + handler: h, + peer: p, + } + h.downloader.RegisterLightPeer(p.id, ethVersion, pc) +} + +func (d *downloaderPeerNotify) unregisterPeer(p *peer) { + h := (*clientHandler)(d) + h.downloader.UnregisterPeer(p.id) +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/clientpool.go b/vendor/github.com/ethereum/go-ethereum/les/clientpool.go index 4ee2fd5da6..0b4d1b9612 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/clientpool.go +++ b/vendor/github.com/ethereum/go-ethereum/les/clientpool.go @@ -17,67 +17,81 @@ package les import ( + "encoding/binary" "io" "math" "sync" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" + "github.com/hashicorp/golang-lru" ) const ( - negBalanceExpTC = time.Hour // time constant for exponentially reducing negative balance - fixedPointMultiplier = 0x1000000 // constant to convert logarithms to fixed point format - connectedBias = time.Minute // this bias is applied in favor of already connected clients in order to avoid kicking them out very soon - lazyQueueRefresh = time.Second * 10 // refresh period of the connected queue -) - -var ( - clientPoolDbKey = []byte("clientPool") - clientBalanceDbKey = []byte("clientPool-balance") + negBalanceExpTC = time.Hour // time constant for exponentially reducing negative balance + fixedPointMultiplier = 0x1000000 // constant to convert logarithms to fixed point format + lazyQueueRefresh = time.Second * 10 // refresh period of the connected queue + persistCumulativeTimeRefresh = time.Minute * 5 // refresh period of the cumulative running time persistence + posBalanceCacheLimit = 8192 // the maximum number of cached items in positive balance queue + negBalanceCacheLimit = 8192 // the maximum number of cached items in negative balance queue + + // connectedBias is applied to already connected clients So that + // already connected client won't be kicked out very soon and we + // can ensure all connected clients can have enough time to request + // or sync some data. + // + // todo(rjl493456442) make it configurable. It can be the option of + // free trial time! + connectedBias = time.Minute * 3 ) // clientPool implements a client database that assigns a priority to each client // based on a positive and negative balance. Positive balance is externally assigned // to prioritized clients and is decreased with connection time and processed // requests (unless the price factors are zero). If the positive balance is zero -// then negative balance is accumulated. Balance tracking and priority calculation -// for connected clients is done by balanceTracker. connectedQueue ensures that -// clients with the lowest positive or highest negative balance get evicted when -// the total capacity allowance is full and new clients with a better balance want -// to connect. Already connected nodes receive a small bias in their favor in order -// to avoid accepting and instantly kicking out clients. -// Balances of disconnected clients are stored in posBalanceQueue and negBalanceQueue -// and are also saved in the database. Negative balance is transformed into a -// logarithmic form with a constantly shifting linear offset in order to implement -// an exponential decrease. negBalanceQueue has a limited size and drops the smallest -// values when necessary. Positive balances are stored in the database as long as -// they exist, posBalanceQueue only acts as a cache for recently accessed entries. +// then negative balance is accumulated. +// +// Balance tracking and priority calculation for connected clients is done by +// balanceTracker. connectedQueue ensures that clients with the lowest positive or +// highest negative balance get evicted when the total capacity allowance is full +// and new clients with a better balance want to connect. +// +// Already connected nodes receive a small bias in their favor in order to avoid +// accepting and instantly kicking out clients. In theory, we try to ensure that +// each client can have several minutes of connection time. +// +// Balances of disconnected clients are stored in nodeDB including positive balance +// and negative banalce. Negative balance is transformed into a logarithmic form +// with a constantly shifting linear offset in order to implement an exponential +// decrease. Besides nodeDB will have a background thread to check the negative +// balance of disconnected client. If the balance is low enough, then the record +// will be dropped. type clientPool struct { - db ethdb.Database + ndb *nodeDB lock sync.Mutex clock mclock.Clock - stopCh chan chan struct{} + stopCh chan struct{} closed bool removePeer func(enode.ID) - queueLimit, countLimit int - freeClientCap, capacityLimit, connectedCapacity uint64 + connectedMap map[enode.ID]*clientInfo + connectedQueue *prque.LazyQueue + + posFactors, negFactors priceFactors - connectedMap map[enode.ID]*clientInfo - posBalanceMap map[enode.ID]*posBalance - negBalanceMap map[string]*negBalance - connectedQueue *prque.LazyQueue - posBalanceQueue, negBalanceQueue *prque.Prque - posFactors, negFactors priceFactors - posBalanceAccessCounter int64 - startupTime mclock.AbsTime - logOffsetAtStartup int64 + connLimit int // The maximum number of connections that clientpool can support + capLimit uint64 // The maximum cumulative capacity that clientpool can support + connectedCap uint64 // The sum of the capacity of the current clientpool connected + freeClientCap uint64 // The capacity value of each free client + startTime mclock.AbsTime // The timestamp at which the clientpool started running + cumulativeTime int64 // The cumulative running time of clientpool at the start point. + disableBias bool // Disable connection bias(used in testing) } // clientPeer represents a client in the pool. @@ -138,22 +152,25 @@ type priceFactors struct { } // newClientPool creates a new client pool -func newClientPool(db ethdb.Database, freeClientCap uint64, queueLimit int, clock mclock.Clock, removePeer func(enode.ID)) *clientPool { +func newClientPool(db ethdb.Database, freeClientCap uint64, clock mclock.Clock, removePeer func(enode.ID)) *clientPool { + ndb := newNodeDB(db, clock) pool := &clientPool{ - db: db, - clock: clock, - connectedMap: make(map[enode.ID]*clientInfo), - posBalanceMap: make(map[enode.ID]*posBalance), - negBalanceMap: make(map[string]*negBalance), - connectedQueue: prque.NewLazyQueue(connSetIndex, connPriority, connMaxPriority, clock, lazyQueueRefresh), - negBalanceQueue: prque.New(negSetIndex), - posBalanceQueue: prque.New(posSetIndex), - freeClientCap: freeClientCap, - queueLimit: queueLimit, - removePeer: removePeer, - stopCh: make(chan chan struct{}), - } - pool.loadFromDb() + ndb: ndb, + clock: clock, + connectedMap: make(map[enode.ID]*clientInfo), + connectedQueue: prque.NewLazyQueue(connSetIndex, connPriority, connMaxPriority, clock, lazyQueueRefresh), + freeClientCap: freeClientCap, + removePeer: removePeer, + startTime: clock.Now(), + cumulativeTime: ndb.getCumulativeTime(), + stopCh: make(chan struct{}), + } + // If the negative balance of free client is even lower than 1, + // delete this entry. + ndb.nbEvictCallBack = func(now mclock.AbsTime, b negBalance) bool { + balance := math.Exp(float64(b.logValue-pool.logOffset(now)) / fixedPointMultiplier) + return balance <= 1 + } go func() { for { select { @@ -161,8 +178,9 @@ func newClientPool(db ethdb.Database, freeClientCap uint64, queueLimit int, cloc pool.lock.Lock() pool.connectedQueue.Refresh() pool.lock.Unlock() - case stop := <-pool.stopCh: - close(stop) + case <-clock.After(persistCumulativeTimeRefresh): + pool.ndb.setCumulativeTime(pool.logOffset(clock.Now())) + case <-pool.stopCh: return } } @@ -172,64 +190,70 @@ func newClientPool(db ethdb.Database, freeClientCap uint64, queueLimit int, cloc // stop shuts the client pool down func (f *clientPool) stop() { - stop := make(chan struct{}) - f.stopCh <- stop - <-stop + close(f.stopCh) f.lock.Lock() f.closed = true - f.saveToDb() f.lock.Unlock() -} - -// registerPeer implements peerSetNotify -func (f *clientPool) registerPeer(p *peer) { - c := f.connect(p, 0) - if c != nil { - p.balanceTracker = &c.balanceTracker - } + f.ndb.setCumulativeTime(f.logOffset(f.clock.Now())) + f.ndb.close() } // connect should be called after a successful handshake. If the connection was // rejected, there is no need to call disconnect. -func (f *clientPool) connect(peer clientPeer, capacity uint64) *clientInfo { +func (f *clientPool) connect(peer clientPeer, capacity uint64) bool { f.lock.Lock() defer f.lock.Unlock() + // Short circuit if clientPool is already closed. if f.closed { - return nil + return false } - address := peer.freeClientId() - id := peer.ID() - idStr := peerIdToString(id) + // Dedup connected peers. + id, freeID := peer.ID(), peer.freeClientId() if _, ok := f.connectedMap[id]; ok { clientRejectedMeter.Mark(1) - log.Debug("Client already connected", "address", address, "id", idStr) - return nil - } - now := f.clock.Now() - // create a clientInfo but do not add it yet - e := &clientInfo{pool: f, peer: peer, address: address, queueIndex: -1, id: id} - posBalance := f.getPosBalance(id).value - e.priority = posBalance != 0 - var negBalance uint64 - nb := f.negBalanceMap[address] - if nb != nil { + log.Debug("Client already connected", "address", freeID, "id", peerIdToString(id)) + return false + } + // Create a clientInfo but do not add it yet + var ( + posBalance uint64 + negBalance uint64 + now = f.clock.Now() + ) + pb := f.ndb.getOrNewPB(id) + posBalance = pb.value + e := &clientInfo{pool: f, peer: peer, address: freeID, queueIndex: -1, id: id, priority: posBalance != 0} + + nb := f.ndb.getOrNewNB(freeID) + if nb.logValue != 0 { negBalance = uint64(math.Exp(float64(nb.logValue-f.logOffset(now)) / fixedPointMultiplier)) + negBalance *= uint64(time.Second) } + // If the client is a free client, assign with a low free capacity, + // Otherwise assign with the given value(priority client) if !e.priority { capacity = f.freeClientCap } - // check whether it fits into connectedQueue + // Ensure the capacity will never lower than the free capacity. if capacity < f.freeClientCap { capacity = f.freeClientCap } e.capacity = capacity + + // Starts a balance tracker e.balanceTracker.init(f.clock, capacity) e.balanceTracker.setBalance(posBalance, negBalance) f.setClientPriceFactors(e) - newCapacity := f.connectedCapacity + capacity + + // If the number of clients already connected in the clientpool exceeds its + // capacity, evict some clients with lowest priority. + // + // If the priority of the newly added client is lower than the priority of + // all connected clients, the client is rejected. + newCapacity := f.connectedCap + capacity newCount := f.connectedQueue.Size() + 1 - if newCapacity > f.capacityLimit || newCount > f.countLimit { + if newCapacity > f.capLimit || newCount > f.connLimit { var ( kickList []*clientInfo kickPriority int64 @@ -240,45 +264,44 @@ func (f *clientPool) connect(peer clientPeer, capacity uint64) *clientInfo { kickPriority = priority newCapacity -= c.capacity newCount-- - return newCapacity > f.capacityLimit || newCount > f.countLimit + return newCapacity > f.capLimit || newCount > f.connLimit }) - if newCapacity > f.capacityLimit || newCount > f.countLimit || (e.balanceTracker.estimatedPriority(now+mclock.AbsTime(connectedBias), false)-kickPriority) > 0 { - // reject client + bias := connectedBias + if f.disableBias { + bias = 0 + } + if newCapacity > f.capLimit || newCount > f.connLimit || (e.balanceTracker.estimatedPriority(now+mclock.AbsTime(bias), false)-kickPriority) > 0 { for _, c := range kickList { f.connectedQueue.Push(c) } clientRejectedMeter.Mark(1) - log.Debug("Client rejected", "address", address, "id", idStr) - return nil + log.Debug("Client rejected", "address", freeID, "id", peerIdToString(id)) + return false } // accept new client, drop old ones for _, c := range kickList { f.dropClient(c, now, true) } } - // client accepted, finish setting it up - if nb != nil { - delete(f.negBalanceMap, address) - f.negBalanceQueue.Remove(nb.queueIndex) - } + // Register new client to connection queue. + f.connectedMap[id] = e + f.connectedQueue.Push(e) + f.connectedCap += e.capacity + + // If the current client is a paid client, monitor the status of client, + // downgrade it to normal client if positive balance is used up. if e.priority { e.balanceTracker.addCallback(balanceCallbackZero, 0, func() { f.balanceExhausted(id) }) } - f.connectedMap[id] = e - f.connectedQueue.Push(e) - f.connectedCapacity += e.capacity - totalConnectedGauge.Update(int64(f.connectedCapacity)) + // If the capacity of client is not the default value(free capacity), notify + // it to update capacity. if e.capacity != f.freeClientCap { e.peer.updateCapacity(e.capacity) } + totalConnectedGauge.Update(int64(f.connectedCap)) clientConnectedMeter.Mark(1) - log.Debug("Client accepted", "address", address) - return e -} - -// unregisterPeer implements peerSetNotify -func (f *clientPool) unregisterPeer(p *peer) { - f.disconnect(p) + log.Debug("Client accepted", "address", freeID) + return true } // disconnect should be called when a connection is terminated. If the disconnection @@ -288,15 +311,14 @@ func (f *clientPool) disconnect(p clientPeer) { f.lock.Lock() defer f.lock.Unlock() + // Short circuit if client pool is already closed. if f.closed { return } - address := p.freeClientId() - id := p.ID() // Short circuit if the peer hasn't been registered. - e := f.connectedMap[id] + e := f.connectedMap[p.ID()] if e == nil { - log.Debug("Client not connected", "address", address, "id", peerIdToString(id)) + log.Debug("Client not connected", "address", p.freeClientId(), "id", peerIdToString(p.ID())) return } f.dropClient(e, f.clock.Now(), false) @@ -311,8 +333,8 @@ func (f *clientPool) dropClient(e *clientInfo, now mclock.AbsTime, kick bool) { f.finalizeBalance(e, now) f.connectedQueue.Remove(e.queueIndex) delete(f.connectedMap, e.id) - f.connectedCapacity -= e.capacity - totalConnectedGauge.Update(int64(f.connectedCapacity)) + f.connectedCap -= e.capacity + totalConnectedGauge.Update(int64(f.connectedCap)) if kick { clientKickedMeter.Mark(1) log.Debug("Client kicked out", "address", e.address) @@ -328,18 +350,17 @@ func (f *clientPool) dropClient(e *clientInfo, now mclock.AbsTime, kick bool) { func (f *clientPool) finalizeBalance(c *clientInfo, now mclock.AbsTime) { c.balanceTracker.stop(now) pos, neg := c.balanceTracker.getBalance(now) - pb := f.getPosBalance(c.id) + + pb, nb := f.ndb.getOrNewPB(c.id), f.ndb.getOrNewNB(c.address) pb.value = pos - f.storePosBalance(pb) - if neg < 1 { - neg = 1 - } - nb := &negBalance{address: c.address, queueIndex: -1, logValue: int64(math.Log(float64(neg))*fixedPointMultiplier) + f.logOffset(now)} - f.negBalanceMap[c.address] = nb - f.negBalanceQueue.Push(nb, -nb.logValue) - if f.negBalanceQueue.Size() > f.queueLimit { - nn := f.negBalanceQueue.PopItem().(*negBalance) - delete(f.negBalanceMap, nn.address) + f.ndb.setPB(c.id, pb) + + neg /= uint64(time.Second) // Convert the expanse to second level. + if neg > 1 { + nb.logValue = int64(math.Log(float64(neg))*fixedPointMultiplier) + f.logOffset(now) + f.ndb.setNB(c.address, nb) + } else { + f.ndb.delNB(c.address) // Negative balance is small enough, drop it directly. } } @@ -355,36 +376,52 @@ func (f *clientPool) balanceExhausted(id enode.ID) { } c.priority = false if c.capacity != f.freeClientCap { - f.connectedCapacity += f.freeClientCap - c.capacity - totalConnectedGauge.Update(int64(f.connectedCapacity)) + f.connectedCap += f.freeClientCap - c.capacity + totalConnectedGauge.Update(int64(f.connectedCap)) c.capacity = f.freeClientCap c.peer.updateCapacity(c.capacity) } + f.ndb.delPB(id) } // setConnLimit sets the maximum number and total capacity of connected clients, // dropping some of them if necessary. -func (f *clientPool) setLimits(count int, totalCap uint64) { +func (f *clientPool) setLimits(totalConn int, totalCap uint64) { f.lock.Lock() defer f.lock.Unlock() - f.countLimit = count - f.capacityLimit = totalCap - now := mclock.Now() - f.connectedQueue.MultiPop(func(data interface{}, priority int64) bool { - c := data.(*clientInfo) - f.dropClient(c, now, true) - return f.connectedCapacity > f.capacityLimit || f.connectedQueue.Size() > f.countLimit - }) + f.connLimit = totalConn + f.capLimit = totalCap + if f.connectedCap > f.capLimit || f.connectedQueue.Size() > f.connLimit { + f.connectedQueue.MultiPop(func(data interface{}, priority int64) bool { + f.dropClient(data.(*clientInfo), mclock.Now(), true) + return f.connectedCap > f.capLimit || f.connectedQueue.Size() > f.connLimit + }) + } +} + +// requestCost feeds request cost after serving a request from the given peer. +func (f *clientPool) requestCost(p *peer, cost uint64) { + f.lock.Lock() + defer f.lock.Unlock() + + info, exist := f.connectedMap[p.ID()] + if !exist || f.closed { + return + } + info.balanceTracker.requestCost(cost) } // logOffset calculates the time-dependent offset for the logarithmic // representation of negative balance +// +// From another point of view, the result returned by the function represents +// the total time that the clientpool is cumulatively running(total_hours/multiplier). func (f *clientPool) logOffset(now mclock.AbsTime) int64 { // Note: fixedPointMultiplier acts as a multiplier here; the reason for dividing the divisor // is to avoid int64 overflow. We assume that int64(negBalanceExpTC) >> fixedPointMultiplier. - logDecay := int64((time.Duration(now - f.startupTime)) / (negBalanceExpTC / fixedPointMultiplier)) - return f.logOffsetAtStartup + logDecay + cumulativeTime := int64((time.Duration(now - f.startTime)) / (negBalanceExpTC / fixedPointMultiplier)) + return f.cumulativeTime + cumulativeTime } // setPriceFactors changes pricing factors for both positive and negative balances. @@ -405,100 +442,6 @@ func (f *clientPool) setClientPriceFactors(c *clientInfo) { c.balanceTracker.setFactors(false, f.posFactors.timeFactor+float64(c.capacity)*f.posFactors.capacityFactor/1000000, f.posFactors.requestFactor) } -// clientPoolStorage is the RLP representation of the pool's database storage -type clientPoolStorage struct { - LogOffset uint64 - List []*negBalance -} - -// loadFromDb restores pool status from the database storage -// (automatically called at initialization) -func (f *clientPool) loadFromDb() { - enc, err := f.db.Get(clientPoolDbKey) - if err != nil { - return - } - var storage clientPoolStorage - err = rlp.DecodeBytes(enc, &storage) - if err != nil { - log.Error("Failed to decode client list", "err", err) - return - } - f.logOffsetAtStartup = int64(storage.LogOffset) - f.startupTime = f.clock.Now() - for _, e := range storage.List { - log.Debug("Loaded free client record", "address", e.address, "logValue", e.logValue) - f.negBalanceMap[e.address] = e - f.negBalanceQueue.Push(e, -e.logValue) - } -} - -// saveToDb saves pool status to the database storage -// (automatically called during shutdown) -func (f *clientPool) saveToDb() { - now := f.clock.Now() - storage := clientPoolStorage{ - LogOffset: uint64(f.logOffset(now)), - } - for _, c := range f.connectedMap { - f.finalizeBalance(c, now) - } - i := 0 - storage.List = make([]*negBalance, len(f.negBalanceMap)) - for _, e := range f.negBalanceMap { - storage.List[i] = e - i++ - } - enc, err := rlp.EncodeToBytes(storage) - if err != nil { - log.Error("Failed to encode negative balance list", "err", err) - } else { - f.db.Put(clientPoolDbKey, enc) - } -} - -// storePosBalance stores a single positive balance entry in the database -func (f *clientPool) storePosBalance(b *posBalance) { - if b.value == b.lastStored { - return - } - enc, err := rlp.EncodeToBytes(b) - if err != nil { - log.Error("Failed to encode client balance", "err", err) - } else { - f.db.Put(append(clientBalanceDbKey, b.id[:]...), enc) - b.lastStored = b.value - } -} - -// getPosBalance retrieves a single positive balance entry from cache or the database -func (f *clientPool) getPosBalance(id enode.ID) *posBalance { - if b, ok := f.posBalanceMap[id]; ok { - f.posBalanceQueue.Remove(b.queueIndex) - f.posBalanceAccessCounter-- - f.posBalanceQueue.Push(b, f.posBalanceAccessCounter) - return b - } - balance := &posBalance{} - if enc, err := f.db.Get(append(clientBalanceDbKey, id[:]...)); err == nil { - if err := rlp.DecodeBytes(enc, balance); err != nil { - log.Error("Failed to decode client balance", "err", err) - balance = &posBalance{} - } - } - balance.id = id - balance.queueIndex = -1 - if f.posBalanceQueue.Size() >= f.queueLimit { - b := f.posBalanceQueue.PopItem().(*posBalance) - f.storePosBalance(b) - delete(f.posBalanceMap, b.id) - } - f.posBalanceAccessCounter-- - f.posBalanceQueue.Push(balance, f.posBalanceAccessCounter) - f.posBalanceMap[id] = balance - return balance -} - // addBalance updates the positive balance of a client. // If setTotal is false then the given amount is added to the balance. // If setTotal is true then amount represents the total amount ever added to the @@ -508,11 +451,21 @@ func (f *clientPool) addBalance(id enode.ID, amount uint64, setTotal bool) { f.lock.Lock() defer f.lock.Unlock() - pb := f.getPosBalance(id) + pb := f.ndb.getOrNewPB(id) c := f.connectedMap[id] - var negBalance uint64 if c != nil { - pb.value, negBalance = c.balanceTracker.getBalance(f.clock.Now()) + posBalance, negBalance := c.balanceTracker.getBalance(f.clock.Now()) + pb.value = posBalance + defer func() { + c.balanceTracker.setBalance(pb.value, negBalance) + if !c.priority && pb.value > 0 { + // The capacity should be adjusted based on the requirement, + // but we have no idea about the new capacity, need a second + // call to udpate it. + c.priority = true + c.balanceTracker.addCallback(balanceCallbackZero, 0, func() { f.balanceExhausted(id) }) + } + }() } if setTotal { if pb.value+amount > pb.lastTotal { @@ -525,21 +478,12 @@ func (f *clientPool) addBalance(id enode.ID, amount uint64, setTotal bool) { pb.value += amount pb.lastTotal += amount } - f.storePosBalance(pb) - if c != nil { - c.balanceTracker.setBalance(pb.value, negBalance) - if !c.priority && pb.value > 0 { - c.priority = true - c.balanceTracker.addCallback(balanceCallbackZero, 0, func() { f.balanceExhausted(id) }) - } - } + f.ndb.setPB(id, pb) } // posBalance represents a recently accessed positive balance entry type posBalance struct { - id enode.ID - value, lastStored, lastTotal uint64 - queueIndex int // position in posBalanceQueue + value, lastTotal uint64 } // EncodeRLP implements rlp.Encoder @@ -556,44 +500,207 @@ func (e *posBalance) DecodeRLP(s *rlp.Stream) error { return err } e.value = entry.Value - e.lastStored = entry.Value e.lastTotal = entry.LastTotal return nil } -// posSetIndex callback updates posBalance item index in posBalanceQueue -func posSetIndex(a interface{}, index int) { - a.(*posBalance).queueIndex = index -} - // negBalance represents a negative balance entry of a disconnected client -type negBalance struct { - address string - logValue int64 - queueIndex int // position in negBalanceQueue -} +type negBalance struct{ logValue int64 } // EncodeRLP implements rlp.Encoder func (e *negBalance) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{e.address, uint64(e.logValue)}) + return rlp.Encode(w, []interface{}{uint64(e.logValue)}) } // DecodeRLP implements rlp.Decoder func (e *negBalance) DecodeRLP(s *rlp.Stream) error { var entry struct { - Address string LogValue uint64 } if err := s.Decode(&entry); err != nil { return err } - e.address = entry.Address e.logValue = int64(entry.LogValue) - e.queueIndex = -1 return nil } -// negSetIndex callback updates negBalance item index in negBalanceQueue -func negSetIndex(a interface{}, index int) { - a.(*negBalance).queueIndex = index +const ( + // nodeDBVersion is the version identifier of the node data in db + nodeDBVersion = 0 + + // dbCleanupCycle is the cycle of db for useless data cleanup + dbCleanupCycle = time.Hour +) + +var ( + positiveBalancePrefix = []byte("pb:") // dbVersion(uint16 big endian) + positiveBalancePrefix + id -> balance + negativeBalancePrefix = []byte("nb:") // dbVersion(uint16 big endian) + negativeBalancePrefix + ip -> balance + cumulativeRunningTimeKey = []byte("cumulativeTime:") // dbVersion(uint16 big endian) + cumulativeRunningTimeKey -> cumulativeTime +) + +type nodeDB struct { + db ethdb.Database + pcache *lru.Cache + ncache *lru.Cache + auxbuf []byte // 37-byte auxiliary buffer for key encoding + verbuf [2]byte // 2-byte auxiliary buffer for db version + nbEvictCallBack func(mclock.AbsTime, negBalance) bool // Callback to determine whether the negative balance can be evicted. + clock mclock.Clock + closeCh chan struct{} + cleanupHook func() // Test hook used for testing +} + +func newNodeDB(db ethdb.Database, clock mclock.Clock) *nodeDB { + pcache, _ := lru.New(posBalanceCacheLimit) + ncache, _ := lru.New(negBalanceCacheLimit) + ndb := &nodeDB{ + db: db, + pcache: pcache, + ncache: ncache, + auxbuf: make([]byte, 37), + clock: clock, + closeCh: make(chan struct{}), + } + binary.BigEndian.PutUint16(ndb.verbuf[:], uint16(nodeDBVersion)) + go ndb.expirer() + return ndb +} + +func (db *nodeDB) close() { + close(db.closeCh) +} + +func (db *nodeDB) key(id []byte, neg bool) []byte { + prefix := positiveBalancePrefix + if neg { + prefix = negativeBalancePrefix + } + if len(prefix)+len(db.verbuf)+len(id) > len(db.auxbuf) { + db.auxbuf = append(db.auxbuf, make([]byte, len(prefix)+len(db.verbuf)+len(id)-len(db.auxbuf))...) + } + copy(db.auxbuf[:len(db.verbuf)], db.verbuf[:]) + copy(db.auxbuf[len(db.verbuf):len(db.verbuf)+len(prefix)], prefix) + copy(db.auxbuf[len(prefix)+len(db.verbuf):len(prefix)+len(db.verbuf)+len(id)], id) + return db.auxbuf[:len(prefix)+len(db.verbuf)+len(id)] +} + +func (db *nodeDB) getCumulativeTime() int64 { + blob, err := db.db.Get(append(cumulativeRunningTimeKey, db.verbuf[:]...)) + if err != nil || len(blob) == 0 { + return 0 + } + return int64(binary.BigEndian.Uint64(blob)) +} + +func (db *nodeDB) setCumulativeTime(v int64) { + binary.BigEndian.PutUint64(db.auxbuf[:8], uint64(v)) + db.db.Put(append(cumulativeRunningTimeKey, db.verbuf[:]...), db.auxbuf[:8]) +} + +func (db *nodeDB) getOrNewPB(id enode.ID) posBalance { + key := db.key(id.Bytes(), false) + item, exist := db.pcache.Get(string(key)) + if exist { + return item.(posBalance) + } + var balance posBalance + if enc, err := db.db.Get(key); err == nil { + if err := rlp.DecodeBytes(enc, &balance); err != nil { + log.Error("Failed to decode positive balance", "err", err) + } + } + db.pcache.Add(string(key), balance) + return balance +} + +func (db *nodeDB) setPB(id enode.ID, b posBalance) { + key := db.key(id.Bytes(), false) + enc, err := rlp.EncodeToBytes(&(b)) + if err != nil { + log.Error("Failed to encode positive balance", "err", err) + return + } + db.db.Put(key, enc) + db.pcache.Add(string(key), b) +} + +func (db *nodeDB) delPB(id enode.ID) { + key := db.key(id.Bytes(), false) + db.db.Delete(key) + db.pcache.Remove(string(key)) +} + +func (db *nodeDB) getOrNewNB(id string) negBalance { + key := db.key([]byte(id), true) + item, exist := db.ncache.Get(string(key)) + if exist { + return item.(negBalance) + } + var balance negBalance + if enc, err := db.db.Get(key); err == nil { + if err := rlp.DecodeBytes(enc, &balance); err != nil { + log.Error("Failed to decode negative balance", "err", err) + } + } + db.ncache.Add(string(key), balance) + return balance +} + +func (db *nodeDB) setNB(id string, b negBalance) { + key := db.key([]byte(id), true) + enc, err := rlp.EncodeToBytes(&(b)) + if err != nil { + log.Error("Failed to encode negative balance", "err", err) + return + } + db.db.Put(key, enc) + db.ncache.Add(string(key), b) +} + +func (db *nodeDB) delNB(id string) { + key := db.key([]byte(id), true) + db.db.Delete(key) + db.ncache.Remove(string(key)) +} + +func (db *nodeDB) expirer() { + for { + select { + case <-db.clock.After(dbCleanupCycle): + db.expireNodes() + case <-db.closeCh: + return + } + } +} + +// expireNodes iterates the whole node db and checks whether the negative balance +// entry can deleted. +// +// The rationale behind this is: server doesn't need to keep the negative balance +// records if they are low enough. +func (db *nodeDB) expireNodes() { + var ( + visited int + deleted int + start = time.Now() + ) + iter := db.db.NewIteratorWithPrefix(append(db.verbuf[:], negativeBalancePrefix...)) + for iter.Next() { + visited += 1 + var balance negBalance + if err := rlp.DecodeBytes(iter.Value(), &balance); err != nil { + log.Error("Failed to decode negative balance", "err", err) + continue + } + if db.nbEvictCallBack != nil && db.nbEvictCallBack(db.clock.Now(), balance) { + deleted += 1 + db.db.Delete(iter.Key()) + } + } + // Invoke testing hook if it's not nil. + if db.cleanupHook != nil { + db.cleanupHook() + } + log.Debug("Expire nodes", "visited", visited, "deleted", deleted, "elapsed", common.PrettyDuration(time.Since(start))) } diff --git a/vendor/github.com/ethereum/go-ethereum/les/commons.go b/vendor/github.com/ethereum/go-ethereum/les/commons.go index ef3c470e58..ad3c5aef3d 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/commons.go +++ b/vendor/github.com/ethereum/go-ethereum/les/commons.go @@ -17,25 +17,56 @@ package les import ( + "fmt" "math/big" + "sync" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" ) +func errResp(code errCode, format string, v ...interface{}) error { + return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) +} + +func lesTopic(genesisHash common.Hash, protocolVersion uint) discv5.Topic { + var name string + switch protocolVersion { + case lpv2: + name = "LES2" + default: + panic(nil) + } + return discv5.Topic(name + "@" + common.Bytes2Hex(genesisHash.Bytes()[0:8])) +} + +type chainReader interface { + CurrentHeader() *types.Header +} + // lesCommons contains fields needed by both server and client. type lesCommons struct { + genesis common.Hash config *eth.Config + chainConfig *params.ChainConfig iConfig *light.IndexerConfig chainDb ethdb.Database - protocolManager *ProtocolManager + peers *peerSet + chainReader chainReader chtIndexer, bloomTrieIndexer *core.ChainIndexer + oracle *checkpointOracle + + closeCh chan struct{} + wg sync.WaitGroup } // NodeInfo represents a short summary of the Ethereum sub-protocol metadata @@ -50,7 +81,7 @@ type NodeInfo struct { } // makeProtocols creates protocol descriptors for the given LES versions. -func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol { +func (c *lesCommons) makeProtocols(versions []uint, runPeer func(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error, peerInfo func(id enode.ID) interface{}) []p2p.Protocol { protos := make([]p2p.Protocol, len(versions)) for i, version := range versions { version := version @@ -59,15 +90,10 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol { Version: version, Length: ProtocolLengths[version], NodeInfo: c.nodeInfo, - Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - return c.protocolManager.runPeer(version, p, rw) - }, - PeerInfo: func(id enode.ID) interface{} { - if p := c.protocolManager.peers.Peer(peerIdToString(id)); p != nil { - return p.Info() - } - return nil + Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error { + return runPeer(version, peer, rw) }, + PeerInfo: peerInfo, } } return protos @@ -75,22 +101,21 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol { // nodeInfo retrieves some protocol metadata about the running host node. func (c *lesCommons) nodeInfo() interface{} { - chain := c.protocolManager.blockchain - head := chain.CurrentHeader() + head := c.chainReader.CurrentHeader() hash := head.Hash() return &NodeInfo{ Network: c.config.NetworkId, - Difficulty: chain.GetTd(hash, head.Number.Uint64()), - Genesis: chain.Genesis().Hash(), - Config: chain.Config(), - Head: chain.CurrentHeader().Hash(), + Difficulty: rawdb.ReadTd(c.chainDb, hash, head.Number.Uint64()), + Genesis: c.genesis, + Config: c.chainConfig, + Head: hash, CHT: c.latestLocalCheckpoint(), } } -// latestLocalCheckpoint finds the common stored section index and returns a set of -// post-processed trie roots (CHT and BloomTrie) associated with -// the appropriate section index and head hash as a local checkpoint package. +// latestLocalCheckpoint finds the common stored section index and returns a set +// of post-processed trie roots (CHT and BloomTrie) associated with the appropriate +// section index and head hash as a local checkpoint package. func (c *lesCommons) latestLocalCheckpoint() params.TrustedCheckpoint { sections, _, _ := c.chtIndexer.Sections() sections2, _, _ := c.bloomTrieIndexer.Sections() @@ -102,15 +127,15 @@ func (c *lesCommons) latestLocalCheckpoint() params.TrustedCheckpoint { // No checkpoint information can be provided. return params.TrustedCheckpoint{} } - return c.getLocalCheckpoint(sections - 1) + return c.localCheckpoint(sections - 1) } -// getLocalCheckpoint returns a set of post-processed trie roots (CHT and BloomTrie) +// localCheckpoint returns a set of post-processed trie roots (CHT and BloomTrie) // associated with the appropriate head hash by specific section index. // // The returned checkpoint is only the checkpoint generated by the local indexers, // not the stable checkpoint registered in the registrar contract. -func (c *lesCommons) getLocalCheckpoint(index uint64) params.TrustedCheckpoint { +func (c *lesCommons) localCheckpoint(index uint64) params.TrustedCheckpoint { sectionHead := c.chtIndexer.SectionHead(index) return params.TrustedCheckpoint{ SectionIndex: index, diff --git a/vendor/github.com/ethereum/go-ethereum/les/costtracker.go b/vendor/github.com/ethereum/go-ethereum/les/costtracker.go index d1bb172e40..81da045660 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/costtracker.go +++ b/vendor/github.com/ethereum/go-ethereum/les/costtracker.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" ) const makeCostStats = false // make request cost statistics during operation @@ -81,12 +82,13 @@ var ( ) const ( - maxCostFactor = 2 // ratio of maximum and average cost estimates + maxCostFactor = 2 // ratio of maximum and average cost estimates + bufLimitRatio = 6000 // fixed bufLimit/MRR ratio gfUsageThreshold = 0.5 gfUsageTC = time.Second gfRaiseTC = time.Second * 200 gfDropTC = time.Second * 50 - gfDbKey = "_globalCostFactorV3" + gfDbKey = "_globalCostFactorV6" ) // costTracker is responsible for calculating costs and cost estimates on the @@ -127,6 +129,10 @@ type costTracker struct { totalRechargeCh chan uint64 stats map[uint64][]uint64 // Used for testing purpose. + + // TestHooks + testing bool // Disable real cost evaluation for testing purpose. + testCostList RequestCostList // Customized cost table for testing purpose. } // newCostTracker creates a cost tracker and loads the cost factor statistics from the database. @@ -221,6 +227,9 @@ type reqInfo struct { // servingTime is the CPU time corresponding to the actual processing of // the request. servingTime float64 + + // msgCode indicates the type of request. + msgCode uint64 } // gfLoop starts an event loop which updates the global cost factor which is @@ -264,10 +273,43 @@ func (ct *costTracker) gfLoop() { for { select { case r := <-ct.reqInfoCh: + relCost := int64(factor * r.servingTime * 100 / r.avgTimeCost) // Convert the value to a percentage form + + // Record more metrics if we are debugging + if metrics.EnabledExpensive { + switch r.msgCode { + case GetBlockHeadersMsg: + relativeCostHeaderHistogram.Update(relCost) + case GetBlockBodiesMsg: + relativeCostBodyHistogram.Update(relCost) + case GetReceiptsMsg: + relativeCostReceiptHistogram.Update(relCost) + case GetCodeMsg: + relativeCostCodeHistogram.Update(relCost) + case GetProofsV2Msg: + relativeCostProofHistogram.Update(relCost) + case GetHelperTrieProofsMsg: + relativeCostHelperProofHistogram.Update(relCost) + case SendTxV2Msg: + relativeCostSendTxHistogram.Update(relCost) + case GetTxStatusMsg: + relativeCostTxStatusHistogram.Update(relCost) + } + } + // SendTxV2 and GetTxStatus requests are two special cases. + // All other requests will only put pressure on the database, and + // the corresponding delay is relatively stable. While these two + // requests involve txpool query, which is usually unstable. + // + // TODO(rjl493456442) fixes this. + if r.msgCode == SendTxV2Msg || r.msgCode == GetTxStatusMsg { + continue + } requestServedMeter.Mark(int64(r.servingTime)) - requestEstimatedMeter.Mark(int64(r.avgTimeCost / factor)) requestServedTimer.Update(time.Duration(r.servingTime)) - relativeCostHistogram.Update(int64(r.avgTimeCost / factor / r.servingTime)) + requestEstimatedMeter.Mark(int64(r.avgTimeCost / factor)) + requestEstimatedTimer.Update(time.Duration(r.avgTimeCost / factor)) + relativeCostHistogram.Update(relCost) now := mclock.Now() dt := float64(now - expUpdate) @@ -318,12 +360,12 @@ func (ct *costTracker) gfLoop() { default: } } + globalFactorGauge.Update(int64(1000 * factor)) log.Debug("global cost factor updated", "factor", factor) } } recentServedGauge.Update(int64(recentTime)) recentEstimatedGauge.Update(int64(recentAvg)) - totalRechargeGauge.Update(int64(totalRecharge)) case <-saveTicker.C: saveCostFactor() @@ -370,7 +412,7 @@ func (ct *costTracker) updateStats(code, amount, servingTime, realCost uint64) { avg := reqAvgTimeCost[code] avgTimeCost := avg.baseCost + amount*avg.reqCost select { - case ct.reqInfoCh <- reqInfo{float64(avgTimeCost), float64(servingTime)}: + case ct.reqInfoCh <- reqInfo{float64(avgTimeCost), float64(servingTime), code}: default: } if makeCostStats { diff --git a/vendor/github.com/ethereum/go-ethereum/les/distributor.go b/vendor/github.com/ethereum/go-ethereum/les/distributor.go index 9235adc03f..6d81149720 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/distributor.go +++ b/vendor/github.com/ethereum/go-ethereum/les/distributor.go @@ -28,14 +28,17 @@ import ( // suitable peers, obeying flow control rules and prioritizing them in creation // order (even when a resend is necessary). type requestDistributor struct { - clock mclock.Clock - reqQueue *list.List - lastReqOrder uint64 - peers map[distPeer]struct{} - peerLock sync.RWMutex - stopChn, loopChn chan struct{} - loopNextSent bool - lock sync.Mutex + clock mclock.Clock + reqQueue *list.List + lastReqOrder uint64 + peers map[distPeer]struct{} + peerLock sync.RWMutex + loopChn chan struct{} + loopNextSent bool + lock sync.Mutex + + closeCh chan struct{} + wg sync.WaitGroup } // distPeer is an LES server peer interface for the request distributor. @@ -66,20 +69,22 @@ type distReq struct { sentChn chan distPeer element *list.Element waitForPeers mclock.AbsTime + enterQueue mclock.AbsTime } // newRequestDistributor creates a new request distributor -func newRequestDistributor(peers *peerSet, stopChn chan struct{}, clock mclock.Clock) *requestDistributor { +func newRequestDistributor(peers *peerSet, clock mclock.Clock) *requestDistributor { d := &requestDistributor{ clock: clock, reqQueue: list.New(), loopChn: make(chan struct{}, 2), - stopChn: stopChn, + closeCh: make(chan struct{}), peers: make(map[distPeer]struct{}), } if peers != nil { peers.notify(d) } + d.wg.Add(1) go d.loop() return d } @@ -105,19 +110,22 @@ func (d *requestDistributor) registerTestPeer(p distPeer) { d.peerLock.Unlock() } -// distMaxWait is the maximum waiting time after which further necessary waiting -// times are recalculated based on new feedback from the servers -const distMaxWait = time.Millisecond * 50 +var ( + // distMaxWait is the maximum waiting time after which further necessary waiting + // times are recalculated based on new feedback from the servers + distMaxWait = time.Millisecond * 50 -// waitForPeers is the time window in which a request does not fail even if it -// has no suitable peers to send to at the moment -const waitForPeers = time.Second * 3 + // waitForPeers is the time window in which a request does not fail even if it + // has no suitable peers to send to at the moment + waitForPeers = time.Second * 3 +) // main event loop func (d *requestDistributor) loop() { + defer d.wg.Done() for { select { - case <-d.stopChn: + case <-d.closeCh: d.lock.Lock() elem := d.reqQueue.Front() for elem != nil { @@ -140,6 +148,7 @@ func (d *requestDistributor) loop() { send := req.request(peer) if send != nil { peer.queueSend(send) + requestSendDelay.Update(time.Duration(d.clock.Now() - req.enterQueue)) } chn <- peer close(chn) @@ -249,6 +258,9 @@ func (d *requestDistributor) queue(r *distReq) chan distPeer { r.reqOrder = d.lastReqOrder r.waitForPeers = d.clock.Now() + mclock.AbsTime(waitForPeers) } + // Assign the timestamp when the request is queued no matter it's + // a new one or re-queued one. + r.enterQueue = d.clock.Now() back := d.reqQueue.Back() if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { @@ -294,3 +306,8 @@ func (d *requestDistributor) remove(r *distReq) { r.element = nil } } + +func (d *requestDistributor) close() { + close(d.closeCh) + d.wg.Wait() +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/enr_entry.go b/vendor/github.com/ethereum/go-ethereum/les/enr_entry.go new file mode 100644 index 0000000000..c2a92dd999 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/enr_entry.go @@ -0,0 +1,32 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "github.com/ethereum/go-ethereum/rlp" +) + +// lesEntry is the "les" ENR entry. This is set for LES servers only. +type lesEntry struct { + // Ignore additional fields (for forward compatibility). + Rest []rlp.RawValue `rlp:"tail"` +} + +// ENRKey implements enr.Entry. +func (e lesEntry) ENRKey() string { + return "les" +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go index 76e4f076a7..df76c56d70 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/fetcher.go +++ b/vendor/github.com/ethereum/go-ethereum/les/fetcher.go @@ -40,9 +40,8 @@ const ( // ODR system to ensure that we only request data related to a certain block from peers who have already processed // and announced that block. type lightFetcher struct { - pm *ProtocolManager - odr *LesOdr - chain lightChain + handler *clientHandler + chain *light.LightChain lock sync.Mutex // lock protects access to the fetcher's internal state variables except sent requests maxConfirmedTd *big.Int @@ -58,13 +57,9 @@ type lightFetcher struct { requestTriggered bool requestTrigger chan struct{} lastTrustedHeader *types.Header -} -// lightChain extends the BlockChain interface by locking. -type lightChain interface { - BlockChain - LockChain() - UnlockChain() + closeCh chan struct{} + wg sync.WaitGroup } // fetcherPeerInfo holds fetcher-specific information about each active peer @@ -114,32 +109,37 @@ type fetchResponse struct { } // newLightFetcher creates a new light fetcher -func newLightFetcher(pm *ProtocolManager) *lightFetcher { +func newLightFetcher(h *clientHandler) *lightFetcher { f := &lightFetcher{ - pm: pm, - chain: pm.blockchain.(*light.LightChain), - odr: pm.odr, + handler: h, + chain: h.backend.blockchain, peers: make(map[*peer]*fetcherPeerInfo), deliverChn: make(chan fetchResponse, 100), requested: make(map[uint64]fetchRequest), timeoutChn: make(chan uint64), requestTrigger: make(chan struct{}, 1), syncDone: make(chan *peer), + closeCh: make(chan struct{}), maxConfirmedTd: big.NewInt(0), } - pm.peers.notify(f) + h.backend.peers.notify(f) - f.pm.wg.Add(1) + f.wg.Add(1) go f.syncLoop() return f } +func (f *lightFetcher) close() { + close(f.closeCh) + f.wg.Wait() +} + // syncLoop is the main event loop of the light fetcher func (f *lightFetcher) syncLoop() { - defer f.pm.wg.Done() + defer f.wg.Done() for { select { - case <-f.pm.quitSync: + case <-f.closeCh: return // request loop keeps running until no further requests are necessary or possible case <-f.requestTrigger: @@ -156,7 +156,7 @@ func (f *lightFetcher) syncLoop() { f.lock.Unlock() if rq != nil { - if _, ok := <-f.pm.reqDist.queue(rq); ok { + if _, ok := <-f.handler.backend.reqDist.queue(rq); ok { if syncing { f.lock.Lock() f.syncing = true @@ -187,9 +187,9 @@ func (f *lightFetcher) syncLoop() { } f.reqMu.Unlock() if ok { - f.pm.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), true) + f.handler.backend.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), true) req.peer.Log().Debug("Fetching data timed out hard") - go f.pm.removePeer(req.peer.id) + go f.handler.removePeer(req.peer.id) } case resp := <-f.deliverChn: f.reqMu.Lock() @@ -202,12 +202,12 @@ func (f *lightFetcher) syncLoop() { } f.reqMu.Unlock() if ok { - f.pm.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), req.timeout) + f.handler.backend.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), req.timeout) } f.lock.Lock() if !ok || !(f.syncing || f.processResponse(req, resp)) { resp.peer.Log().Debug("Failed processing response") - go f.pm.removePeer(resp.peer.id) + go f.handler.removePeer(resp.peer.id) } f.lock.Unlock() case p := <-f.syncDone: @@ -264,7 +264,7 @@ func (f *lightFetcher) announce(p *peer, head *announceData) { if fp.lastAnnounced != nil && head.Td.Cmp(fp.lastAnnounced.td) <= 0 { // announced tds should be strictly monotonic p.Log().Debug("Received non-monotonic td", "current", head.Td, "previous", fp.lastAnnounced.td) - go f.pm.removePeer(p.id) + go f.handler.removePeer(p.id) return } @@ -297,7 +297,7 @@ func (f *lightFetcher) announce(p *peer, head *announceData) { // if one of root's children is canonical, keep it, delete other branches and root itself var newRoot *fetcherTreeNode for i, nn := range fp.root.children { - if rawdb.ReadCanonicalHash(f.pm.chainDb, nn.number) == nn.hash { + if rawdb.ReadCanonicalHash(f.handler.backend.chainDb, nn.number) == nn.hash { fp.root.children = append(fp.root.children[:i], fp.root.children[i+1:]...) nn.parent = nil newRoot = nn @@ -390,7 +390,7 @@ func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64, ha // // when syncing, just check if it is part of the known chain, there is nothing better we // can do since we do not know the most recent block hash yet - return rawdb.ReadCanonicalHash(f.pm.chainDb, fp.root.number) == fp.root.hash && rawdb.ReadCanonicalHash(f.pm.chainDb, number) == hash + return rawdb.ReadCanonicalHash(f.handler.backend.chainDb, fp.root.number) == fp.root.hash && rawdb.ReadCanonicalHash(f.handler.backend.chainDb, number) == hash } // requestAmount calculates the amount of headers to be downloaded starting @@ -453,8 +453,7 @@ func (f *lightFetcher) findBestRequest() (bestHash common.Hash, bestAmount uint6 if f.checkKnownNode(p, n) || n.requested { continue } - - //if ulc mode is disabled, isTrustedHash returns true + // if ulc mode is disabled, isTrustedHash returns true amount := f.requestAmount(p, n) if (bestTd == nil || n.td.Cmp(bestTd) > 0 || amount < bestAmount) && (f.isTrustedHash(hash) || f.maxConfirmedTd.Int64() == 0) { bestHash = hash @@ -470,7 +469,7 @@ func (f *lightFetcher) findBestRequest() (bestHash common.Hash, bestAmount uint6 // isTrustedHash checks if the block can be trusted by the minimum trusted fraction. func (f *lightFetcher) isTrustedHash(hash common.Hash) bool { // If ultra light cliet mode is disabled, trust all hashes - if f.pm.ulc == nil { + if f.handler.ulc == nil { return true } // Ultra light enabled, only trust after enough confirmations @@ -480,7 +479,7 @@ func (f *lightFetcher) isTrustedHash(hash common.Hash) bool { agreed++ } } - return 100*agreed/len(f.pm.ulc.keys) >= f.pm.ulc.fraction + return 100*agreed/len(f.handler.ulc.keys) >= f.handler.ulc.fraction } func (f *lightFetcher) newFetcherDistReqForSync(bestHash common.Hash) *distReq { @@ -500,14 +499,14 @@ func (f *lightFetcher) newFetcherDistReqForSync(bestHash common.Hash) *distReq { return fp != nil && fp.nodeByHash[bestHash] != nil }, request: func(dp distPeer) func() { - if f.pm.ulc != nil { + if f.handler.ulc != nil { // Keep last trusted header before sync f.setLastTrustedHeader(f.chain.CurrentHeader()) } go func() { p := dp.(*peer) p.Log().Debug("Synchronisation started") - f.pm.synchronise(p) + f.handler.synchronise(p) f.syncDone <- p }() return nil @@ -607,7 +606,7 @@ func (f *lightFetcher) newHeaders(headers []*types.Header, tds []*big.Int) { for p, fp := range f.peers { if !f.checkAnnouncedHeaders(fp, headers, tds) { p.Log().Debug("Inconsistent announcement") - go f.pm.removePeer(p.id) + go f.handler.removePeer(p.id) } if fp.confirmedTd != nil && (maxTd == nil || maxTd.Cmp(fp.confirmedTd) > 0) { maxTd = fp.confirmedTd @@ -705,7 +704,7 @@ func (f *lightFetcher) checkSyncedHeaders(p *peer) { node = fp.lastAnnounced td *big.Int ) - if f.pm.ulc != nil { + if f.handler.ulc != nil { // Roll back untrusted blocks h, unapproved := f.lastTrustedTreeNode(p) f.chain.Rollback(unapproved) @@ -721,7 +720,7 @@ func (f *lightFetcher) checkSyncedHeaders(p *peer) { // Now node is the latest downloaded/approved header after syncing if node == nil { p.Log().Debug("Synchronisation failed") - go f.pm.removePeer(p.id) + go f.handler.removePeer(p.id) return } header := f.chain.GetHeader(node.hash, node.number) @@ -741,7 +740,7 @@ func (f *lightFetcher) lastTrustedTreeNode(p *peer) (*types.Header, []common.Has if canonical.Number.Uint64() > f.lastTrustedHeader.Number.Uint64() { canonical = f.chain.GetHeaderByNumber(f.lastTrustedHeader.Number.Uint64()) } - commonAncestor := rawdb.FindCommonAncestor(f.pm.chainDb, canonical, f.lastTrustedHeader) + commonAncestor := rawdb.FindCommonAncestor(f.handler.backend.chainDb, canonical, f.lastTrustedHeader) if commonAncestor == nil { log.Error("Common ancestor of last trusted header and canonical header is nil", "canonical hash", canonical.Hash(), "trusted hash", f.lastTrustedHeader.Hash()) return current, unapprovedHashes @@ -787,7 +786,7 @@ func (f *lightFetcher) checkKnownNode(p *peer, n *fetcherTreeNode) bool { } if !f.checkAnnouncedHeaders(fp, []*types.Header{header}, []*big.Int{td}) { p.Log().Debug("Inconsistent announcement") - go f.pm.removePeer(p.id) + go f.handler.removePeer(p.id) } if fp.confirmedTd != nil { f.updateMaxConfirmedTd(fp.confirmedTd) @@ -880,12 +879,12 @@ func (f *lightFetcher) checkUpdateStats(p *peer, newEntry *updateStatsEntry) { fp.firstUpdateStats = newEntry } for fp.firstUpdateStats != nil && fp.firstUpdateStats.time <= now-mclock.AbsTime(blockDelayTimeout) { - f.pm.serverPool.adjustBlockDelay(p.poolEntry, blockDelayTimeout) + f.handler.backend.serverPool.adjustBlockDelay(p.poolEntry, blockDelayTimeout) fp.firstUpdateStats = fp.firstUpdateStats.next } if fp.confirmedTd != nil { for fp.firstUpdateStats != nil && fp.firstUpdateStats.td.Cmp(fp.confirmedTd) <= 0 { - f.pm.serverPool.adjustBlockDelay(p.poolEntry, time.Duration(now-fp.firstUpdateStats.time)) + f.handler.backend.serverPool.adjustBlockDelay(p.poolEntry, time.Duration(now-fp.firstUpdateStats.time)) fp.firstUpdateStats = fp.firstUpdateStats.next } } diff --git a/vendor/github.com/ethereum/go-ethereum/les/handler.go b/vendor/github.com/ethereum/go-ethereum/les/handler.go deleted file mode 100644 index 807065e55d..0000000000 --- a/vendor/github.com/ethereum/go-ethereum/les/handler.go +++ /dev/null @@ -1,1293 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "math/big" - "sync" - "sync/atomic" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" -) - -var errTooManyInvalidRequest = errors.New("too many invalid requests made") - -const ( - softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. - estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header - - ethVersion = 63 // equivalent eth version for the downloader - - MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request - MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request - MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request - MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request - MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request - MaxHelperTrieProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request - MaxTxSend = 64 // Amount of transactions to be send per request - MaxTxStatus = 256 // Amount of transactions to queried per request - - disableClientRemovePeer = false -) - -func errResp(code errCode, format string, v ...interface{}) error { - return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) -} - -type BlockChain interface { - Config() *params.ChainConfig - HasHeader(hash common.Hash, number uint64) bool - GetHeader(hash common.Hash, number uint64) *types.Header - GetHeaderByHash(hash common.Hash) *types.Header - CurrentHeader() *types.Header - GetTd(hash common.Hash, number uint64) *big.Int - StateCache() state.Database - InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) - Rollback(chain []common.Hash) - GetHeaderByNumber(number uint64) *types.Header - GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) - Genesis() *types.Block - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription -} - -type txPool interface { - AddRemotes(txs []*types.Transaction) []error - AddRemotesSync(txs []*types.Transaction) []error - Status(hashes []common.Hash) []core.TxStatus -} - -type ProtocolManager struct { - // Configs - chainConfig *params.ChainConfig - iConfig *light.IndexerConfig - - client bool // The indicator whether the node is light client - maxPeers int // The maximum number peers allowed to connect. - networkId uint64 // The identity of network. - - txpool txPool - txrelay *lesTxRelay - blockchain BlockChain - chainDb ethdb.Database - odr *LesOdr - server *LesServer - serverPool *serverPool - lesTopic discv5.Topic - reqDist *requestDistributor - retriever *retrieveManager - servingQueue *servingQueue - downloader *downloader.Downloader - fetcher *lightFetcher - ulc *ulc - peers *peerSet - checkpoint *params.TrustedCheckpoint - reg *checkpointOracle // If reg == nil, it means the checkpoint registrar is not activated - - // channels for fetcher, syncer, txsyncLoop - newPeerCh chan *peer - quitSync chan struct{} - noMorePeers chan struct{} - - wg *sync.WaitGroup - eventMux *event.TypeMux - - // Callbacks - synced func() bool - - // Testing fields - addTxsSync bool -} - -// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable -// with the ethereum network. -func NewProtocolManager(chainConfig *params.ChainConfig, checkpoint *params.TrustedCheckpoint, indexerConfig *light.IndexerConfig, ulcServers []string, ulcFraction int, client bool, networkId uint64, mux *event.TypeMux, peers *peerSet, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, serverPool *serverPool, registrar *checkpointOracle, quitSync chan struct{}, wg *sync.WaitGroup, synced func() bool) (*ProtocolManager, error) { - // Create the protocol manager with the base fields - manager := &ProtocolManager{ - client: client, - eventMux: mux, - blockchain: blockchain, - chainConfig: chainConfig, - iConfig: indexerConfig, - chainDb: chainDb, - odr: odr, - networkId: networkId, - txpool: txpool, - serverPool: serverPool, - reg: registrar, - peers: peers, - newPeerCh: make(chan *peer), - quitSync: quitSync, - wg: wg, - noMorePeers: make(chan struct{}), - checkpoint: checkpoint, - synced: synced, - } - if odr != nil { - manager.retriever = odr.retriever - manager.reqDist = odr.retriever.dist - } - - if ulcServers != nil { - ulc, err := newULC(ulcServers, ulcFraction) - if err != nil { - log.Warn("Failed to initialize ultra light client", "err", err) - } else { - manager.ulc = ulc - } - } - removePeer := manager.removePeer - if disableClientRemovePeer { - removePeer = func(id string) {} - } - if client { - var checkpointNumber uint64 - if checkpoint != nil { - checkpointNumber = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1 - } - manager.downloader = downloader.New(checkpointNumber, chainDb, nil, manager.eventMux, nil, blockchain, removePeer) - manager.peers.notify((*downloaderPeerNotify)(manager)) - manager.fetcher = newLightFetcher(manager) - } - return manager, nil -} - -// removePeer initiates disconnection from a peer by removing it from the peer set -func (pm *ProtocolManager) removePeer(id string) { - pm.peers.Unregister(id) -} - -func (pm *ProtocolManager) Start(maxPeers int) { - pm.maxPeers = maxPeers - if pm.client { - go pm.syncer() - } else { - go func() { - for range pm.newPeerCh { - } - }() - } -} - -func (pm *ProtocolManager) Stop() { - // Showing a log message. During download / process this could actually - // take between 5 to 10 seconds and therefor feedback is required. - log.Info("Stopping light Ethereum protocol") - - // Quit the sync loop. - // After this send has completed, no new peers will be accepted. - pm.noMorePeers <- struct{}{} - - close(pm.quitSync) // quits syncer, fetcher - - if pm.servingQueue != nil { - pm.servingQueue.stop() - } - - // Disconnect existing sessions. - // This also closes the gate for any new registrations on the peer set. - // sessions which are already established but not added to pm.peers yet - // will exit when they try to register. - pm.peers.Close() - - // Wait for any process action - pm.wg.Wait() - - log.Info("Light Ethereum protocol stopped") -} - -// runPeer is the p2p protocol run function for the given version. -func (pm *ProtocolManager) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error { - var entry *poolEntry - peer := pm.newPeer(int(version), pm.networkId, p, rw) - if pm.serverPool != nil { - entry = pm.serverPool.connect(peer, peer.Node()) - } - peer.poolEntry = entry - select { - case pm.newPeerCh <- peer: - pm.wg.Add(1) - defer pm.wg.Done() - err := pm.handle(peer) - if entry != nil { - pm.serverPool.disconnect(entry) - } - return err - case <-pm.quitSync: - if entry != nil { - pm.serverPool.disconnect(entry) - } - return p2p.DiscQuitting - } -} - -func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { - var trusted bool - if pm.ulc != nil { - trusted = pm.ulc.trusted(p.ID()) - } - return newPeer(pv, nv, trusted, p, newMeteredMsgWriter(rw)) -} - -// handle is the callback invoked to manage the life cycle of a les peer. When -// this function terminates, the peer is disconnected. -func (pm *ProtocolManager) handle(p *peer) error { - // Ignore maxPeers if this is a trusted peer - // In server mode we try to check into the client pool after handshake - if pm.client && pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted { - clientRejectedMeter.Mark(1) - return p2p.DiscTooManyPeers - } - // Reject light clients if server is not synced. - if !pm.client && !pm.synced() { - clientRejectedMeter.Mark(1) - return p2p.DiscRequested - } - p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) - - // Execute the LES handshake - var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() - hash = head.Hash() - number = head.Number.Uint64() - td = pm.blockchain.GetTd(hash, number) - ) - if err := p.Handshake(td, hash, number, genesis.Hash(), pm.server); err != nil { - p.Log().Debug("Light Ethereum handshake failed", "err", err) - clientErrorMeter.Mark(1) - return err - } - if p.fcClient != nil { - defer p.fcClient.Disconnect() - } - - if rw, ok := p.rw.(*meteredMsgReadWriter); ok { - rw.Init(p.version) - } - - // Register the peer locally - if err := pm.peers.Register(p); err != nil { - clientErrorMeter.Mark(1) - p.Log().Error("Light Ethereum peer registration failed", "err", err) - return err - } - if !pm.client && p.balanceTracker == nil { - // add dummy balance tracker for tests - p.balanceTracker = &balanceTracker{} - p.balanceTracker.init(&mclock.System{}, 1) - } - connectedAt := time.Now() - defer func() { - p.balanceTracker = nil - pm.removePeer(p.id) - connectionTimer.UpdateSince(connectedAt) - }() - - // Register the peer in the downloader. If the downloader considers it banned, we disconnect - if pm.client { - p.lock.Lock() - head := p.headInfo - p.lock.Unlock() - if pm.fetcher != nil { - pm.fetcher.announce(p, head) - } - - if p.poolEntry != nil { - pm.serverPool.registered(p.poolEntry) - } - } - // main loop. handle incoming messages. - for { - if err := pm.handleMsg(p); err != nil { - p.Log().Debug("Light Ethereum message handling failed", "err", err) - if p.fcServer != nil { - p.fcServer.DumpLogs() - } - return err - } - } -} - -// handleMsg is invoked whenever an inbound message is received from a remote -// peer. The remote connection is torn down upon returning any error. -func (pm *ProtocolManager) handleMsg(p *peer) error { - select { - case err := <-p.errCh: - return err - default: - } - // Read the next message from the remote peer, and ensure it's fully consumed - msg, err := p.rw.ReadMsg() - if err != nil { - return err - } - p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size) - - p.responseCount++ - responseCount := p.responseCount - var ( - maxCost uint64 - task *servingTask - ) - - accept := func(reqID, reqCnt, maxCnt uint64) bool { - inSizeCost := func() uint64 { - if pm.server.costTracker != nil { - return pm.server.costTracker.realCost(0, msg.Size, 0) - } - return 0 - } - if p.isFrozen() || reqCnt == 0 || p.fcClient == nil || reqCnt > maxCnt { - p.fcClient.OneTimeCost(inSizeCost()) - return false - } - maxCost = p.fcCosts.getMaxCost(msg.Code, reqCnt) - gf := float64(1) - if pm.server.costTracker != nil { - gf = pm.server.costTracker.globalFactor() - if gf < 0.001 { - p.Log().Error("Invalid global cost factor", "globalFactor", gf) - gf = 1 - } - } - maxTime := uint64(float64(maxCost) / gf) - - if accepted, bufShort, servingPriority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost); !accepted { - p.freezeClient() - p.Log().Warn("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge))) - p.fcClient.OneTimeCost(inSizeCost()) - return false - } else { - task = pm.servingQueue.newTask(p, maxTime, servingPriority) - } - if task.start() { - return true - } - p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost()) - return false - } - - if msg.Size > ProtocolMaxMsgSize { - return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) - } - defer msg.Discard() - - var deliverMsg *Msg - balanceTracker := p.balanceTracker - - sendResponse := func(reqID, amount uint64, reply *reply, servingTime uint64) { - p.responseLock.Lock() - defer p.responseLock.Unlock() - - if p.isFrozen() { - amount = 0 - reply = nil - } - var replySize uint32 - if reply != nil { - replySize = reply.size() - } - var realCost uint64 - if pm.server.costTracker != nil { - realCost = pm.server.costTracker.realCost(servingTime, msg.Size, replySize) - if amount != 0 { - pm.server.costTracker.updateStats(msg.Code, amount, servingTime, realCost) - balanceTracker.requestCost(realCost) - } - } else { - realCost = maxCost - } - bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) - if reply != nil { - p.queueSend(func() { - if err := reply.send(bv); err != nil { - select { - case p.errCh <- err: - default: - } - } - }) - } - } - - // Handle the message depending on its contents - switch msg.Code { - case StatusMsg: - p.Log().Trace("Received status message") - // Status messages should never arrive after the handshake - return errResp(ErrExtraStatusMsg, "uncontrolled status message") - - // Block header query, collect the requested headers and reply - case AnnounceMsg: - p.Log().Trace("Received announce message") - var req announceData - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - if err := req.sanityCheck(); err != nil { - return err - } - update, size := req.Update.decode() - if p.rejectUpdate(size) { - return errResp(ErrRequestRejected, "") - } - p.updateFlowControl(update) - - if req.Hash != (common.Hash{}) { - if p.announceType == announceTypeNone { - return errResp(ErrUnexpectedResponse, "") - } - if p.announceType == announceTypeSigned { - if err := req.checkSignature(p.ID(), update); err != nil { - p.Log().Trace("Invalid announcement signature", "err", err) - return err - } - p.Log().Trace("Valid announcement signature") - } - - p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth) - if pm.fetcher != nil { - pm.fetcher.announce(p, &req) - } - } - - case GetBlockHeadersMsg: - p.Log().Trace("Received block header request") - // Decode the complex header query - var req struct { - ReqID uint64 - Query getBlockHeadersData - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - - query := req.Query - if accept(req.ReqID, query.Amount, MaxHeaderFetch) { - go func() { - hashMode := query.Origin.Hash != (common.Hash{}) - first := true - maxNonCanonical := uint64(100) - - // Gather headers until the fetch or network limits is reached - var ( - bytes common.StorageSize - headers []*types.Header - unknown bool - ) - for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit { - if !first && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Retrieve the next header satisfying the query - var origin *types.Header - if hashMode { - if first { - origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash) - if origin != nil { - query.Origin.Number = origin.Number.Uint64() - } - } else { - origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number) - } - } else { - origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) - } - if origin == nil { - atomic.AddUint32(&p.invalidCount, 1) - break - } - headers = append(headers, origin) - bytes += estHeaderRlpSize - - // Advance to the next header of the query - switch { - case hashMode && query.Reverse: - // Hash based traversal towards the genesis block - ancestor := query.Skip + 1 - if ancestor == 0 { - unknown = true - } else { - query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical) - unknown = (query.Origin.Hash == common.Hash{}) - } - case hashMode && !query.Reverse: - // Hash based traversal towards the leaf block - var ( - current = origin.Number.Uint64() - next = current + query.Skip + 1 - ) - if next <= current { - infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") - p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", query.Skip, "next", next, "attacker", infos) - unknown = true - } else { - if header := pm.blockchain.GetHeaderByNumber(next); header != nil { - nextHash := header.Hash() - expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical) - if expOldHash == query.Origin.Hash { - query.Origin.Hash, query.Origin.Number = nextHash, next - } else { - unknown = true - } - } else { - unknown = true - } - } - case query.Reverse: - // Number based traversal towards the genesis block - if query.Origin.Number >= query.Skip+1 { - query.Origin.Number -= query.Skip + 1 - } else { - unknown = true - } - case !query.Reverse: - // Number based traversal towards the leaf block - query.Origin.Number += query.Skip + 1 - } - first = false - } - sendResponse(req.ReqID, query.Amount, p.ReplyBlockHeaders(req.ReqID, headers), task.done()) - }() - } - - case BlockHeadersMsg: - if pm.downloader == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received block header response message") - // A batch of headers arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Headers []*types.Header - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - if pm.fetcher != nil && pm.fetcher.requestedID(resp.ReqID) { - pm.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) - } else { - err := pm.downloader.DeliverHeaders(p.id, resp.Headers) - if err != nil { - log.Debug(fmt.Sprint(err)) - } - } - - case GetBlockBodiesMsg: - p.Log().Trace("Received block bodies request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather blocks until the fetch or network limits is reached - var ( - bytes int - bodies []rlp.RawValue - ) - reqCnt := len(req.Hashes) - if accept(req.ReqID, uint64(reqCnt), MaxBodyFetch) { - go func() { - for i, hash := range req.Hashes { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Retrieve the requested block body, stopping if enough was found - if bytes >= softResponseLimit { - break - } - number := rawdb.ReadHeaderNumber(pm.chainDb, hash) - if number == nil { - atomic.AddUint32(&p.invalidCount, 1) - continue - } - if data := rawdb.ReadBodyRLP(pm.chainDb, hash, *number); len(data) != 0 { - bodies = append(bodies, data) - bytes += len(data) - } - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyBlockBodiesRLP(req.ReqID, bodies), task.done()) - }() - } - - case BlockBodiesMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received block bodies response") - // A batch of block bodies arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data []*types.Body - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgBlockBodies, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case GetCodeMsg: - p.Log().Trace("Received code request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []CodeReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - bytes int - data [][]byte - ) - reqCnt := len(req.Reqs) - if accept(req.ReqID, uint64(reqCnt), MaxCodeFetch) { - go func() { - for i, request := range req.Reqs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Look up the root hash belonging to the request - number := rawdb.ReadHeaderNumber(pm.chainDb, request.BHash) - if number == nil { - p.Log().Warn("Failed to retrieve block num for code", "hash", request.BHash) - atomic.AddUint32(&p.invalidCount, 1) - continue - } - header := rawdb.ReadHeader(pm.chainDb, request.BHash, *number) - if header == nil { - p.Log().Warn("Failed to retrieve header for code", "block", *number, "hash", request.BHash) - continue - } - // Refuse to search stale state data in the database since looking for - // a non-exist key is kind of expensive. - local := pm.blockchain.CurrentHeader().Number.Uint64() - if !pm.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { - p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) - atomic.AddUint32(&p.invalidCount, 1) - continue - } - triedb := pm.blockchain.StateCache().TrieDB() - - account, err := pm.getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) - if err != nil { - p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) - atomic.AddUint32(&p.invalidCount, 1) - continue - } - code, err := triedb.Node(common.BytesToHash(account.CodeHash)) - if err != nil { - p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) - continue - } - // Accumulate the code and abort if enough data was retrieved - data = append(data, code) - if bytes += len(code); bytes >= softResponseLimit { - break - } - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyCode(req.ReqID, data), task.done()) - }() - } - - case CodeMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received code response") - // A batch of node state data arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data [][]byte - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgCode, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case GetReceiptsMsg: - p.Log().Trace("Received receipts request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - bytes int - receipts []rlp.RawValue - ) - reqCnt := len(req.Hashes) - if accept(req.ReqID, uint64(reqCnt), MaxReceiptFetch) { - go func() { - for i, hash := range req.Hashes { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - if bytes >= softResponseLimit { - break - } - // Retrieve the requested block's receipts, skipping if unknown to us - var results types.Receipts - number := rawdb.ReadHeaderNumber(pm.chainDb, hash) - if number == nil { - atomic.AddUint32(&p.invalidCount, 1) - continue - } - results = rawdb.ReadRawReceipts(pm.chainDb, hash, *number) - if results == nil { - if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { - continue - } - } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(results); err != nil { - log.Error("Failed to encode receipt", "err", err) - } else { - receipts = append(receipts, encoded) - bytes += len(encoded) - } - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyReceiptsRLP(req.ReqID, receipts), task.done()) - }() - } - - case ReceiptsMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received receipts response") - // A batch of receipts arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Receipts []types.Receipts - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgReceipts, - ReqID: resp.ReqID, - Obj: resp.Receipts, - } - - case GetProofsV2Msg: - p.Log().Trace("Received les/2 proofs request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []ProofReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - lastBHash common.Hash - root common.Hash - ) - reqCnt := len(req.Reqs) - if accept(req.ReqID, uint64(reqCnt), MaxProofsFetch) { - go func() { - nodes := light.NewNodeSet() - - for i, request := range req.Reqs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - // Look up the root hash belonging to the request - var ( - number *uint64 - header *types.Header - trie state.Trie - ) - if request.BHash != lastBHash { - root, lastBHash = common.Hash{}, request.BHash - - if number = rawdb.ReadHeaderNumber(pm.chainDb, request.BHash); number == nil { - p.Log().Warn("Failed to retrieve block num for proof", "hash", request.BHash) - atomic.AddUint32(&p.invalidCount, 1) - continue - } - if header = rawdb.ReadHeader(pm.chainDb, request.BHash, *number); header == nil { - p.Log().Warn("Failed to retrieve header for proof", "block", *number, "hash", request.BHash) - continue - } - // Refuse to search stale state data in the database since looking for - // a non-exist key is kind of expensive. - local := pm.blockchain.CurrentHeader().Number.Uint64() - if !pm.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { - p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) - atomic.AddUint32(&p.invalidCount, 1) - continue - } - root = header.Root - } - // If a header lookup failed (non existent), ignore subsequent requests for the same header - if root == (common.Hash{}) { - atomic.AddUint32(&p.invalidCount, 1) - continue - } - // Open the account or storage trie for the request - statedb := pm.blockchain.StateCache() - - switch len(request.AccKey) { - case 0: - // No account key specified, open an account trie - trie, err = statedb.OpenTrie(root) - if trie == nil || err != nil { - p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err) - continue - } - default: - // Account key specified, open a storage trie - account, err := pm.getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) - if err != nil { - p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) - atomic.AddUint32(&p.invalidCount, 1) - continue - } - trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) - if trie == nil || err != nil { - p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err) - continue - } - } - // Prove the user's request from the account or stroage trie - if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil { - p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err) - continue - } - if nodes.DataSize() >= softResponseLimit { - break - } - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyProofsV2(req.ReqID, nodes.NodeList()), task.done()) - }() - } - - case ProofsV2Msg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received les/2 proofs response") - // A batch of merkle proofs arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data light.NodeList - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgProofsV2, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case GetHelperTrieProofsMsg: - p.Log().Trace("Received helper trie proof request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []HelperTrieReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - auxBytes int - auxData [][]byte - ) - reqCnt := len(req.Reqs) - if accept(req.ReqID, uint64(reqCnt), MaxHelperTrieProofsFetch) { - go func() { - - var ( - lastIdx uint64 - lastType uint - root common.Hash - auxTrie *trie.Trie - ) - nodes := light.NewNodeSet() - for i, request := range req.Reqs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx { - auxTrie, lastType, lastIdx = nil, request.Type, request.TrieIdx - - var prefix string - if root, prefix = pm.getHelperTrie(request.Type, request.TrieIdx); root != (common.Hash{}) { - auxTrie, _ = trie.New(root, trie.NewDatabase(rawdb.NewTable(pm.chainDb, prefix))) - } - } - if request.AuxReq == auxRoot { - var data []byte - if root != (common.Hash{}) { - data = root[:] - } - auxData = append(auxData, data) - auxBytes += len(data) - } else { - if auxTrie != nil { - auxTrie.Prove(request.Key, request.FromLevel, nodes) - } - if request.AuxReq != 0 { - data := pm.getHelperTrieAuxData(request) - auxData = append(auxData, data) - auxBytes += len(data) - } - } - if nodes.DataSize()+auxBytes >= softResponseLimit { - break - } - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyHelperTrieProofs(req.ReqID, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData}), task.done()) - }() - } - - case HelperTrieProofsMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received helper trie proof response") - var resp struct { - ReqID, BV uint64 - Data HelperTrieResps - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgHelperTrieProofs, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case SendTxV2Msg: - if pm.txpool == nil { - return errResp(ErrRequestRejected, "") - } - // Transactions arrived, parse all of them and deliver to the pool - var req struct { - ReqID uint64 - Txs []*types.Transaction - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(req.Txs) - if accept(req.ReqID, uint64(reqCnt), MaxTxSend) { - go func() { - stats := make([]light.TxStatus, len(req.Txs)) - for i, tx := range req.Txs { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - hash := tx.Hash() - stats[i] = pm.txStatus(hash) - if stats[i].Status == core.TxStatusUnknown { - addFn := pm.txpool.AddRemotes - // Add txs synchronously for testing purpose - if pm.addTxsSync { - addFn = pm.txpool.AddRemotesSync - } - if errs := addFn([]*types.Transaction{tx}); errs[0] != nil { - stats[i].Error = errs[0].Error() - continue - } - stats[i] = pm.txStatus(hash) - } - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyTxStatus(req.ReqID, stats), task.done()) - }() - } - - case GetTxStatusMsg: - if pm.txpool == nil { - return errResp(ErrUnexpectedResponse, "") - } - // Transactions arrived, parse all of them and deliver to the pool - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(req.Hashes) - if accept(req.ReqID, uint64(reqCnt), MaxTxStatus) { - go func() { - stats := make([]light.TxStatus, len(req.Hashes)) - for i, hash := range req.Hashes { - if i != 0 && !task.waitOrStop() { - sendResponse(req.ReqID, 0, nil, task.servingTime) - return - } - stats[i] = pm.txStatus(hash) - } - sendResponse(req.ReqID, uint64(reqCnt), p.ReplyTxStatus(req.ReqID, stats), task.done()) - }() - } - - case TxStatusMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received tx status response") - var resp struct { - ReqID, BV uint64 - Status []light.TxStatus - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - - p.fcServer.ReceivedReply(resp.ReqID, resp.BV) - - p.Log().Trace("Received helper trie proof response") - deliverMsg = &Msg{ - MsgType: MsgTxStatus, - ReqID: resp.ReqID, - Obj: resp.Status, - } - - case StopMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - p.freezeServer(true) - pm.retriever.frozen(p) - p.Log().Debug("Service stopped") - - case ResumeMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - var bv uint64 - if err := msg.Decode(&bv); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.ResumeFreeze(bv) - p.freezeServer(false) - p.Log().Debug("Service resumed") - - default: - p.Log().Trace("Received unknown message", "code", msg.Code) - return errResp(ErrInvalidMsgCode, "%v", msg.Code) - } - - if deliverMsg != nil { - err := pm.retriever.deliver(p, deliverMsg) - if err != nil { - p.responseErrors++ - if p.responseErrors > maxResponseErrors { - return err - } - } - } - // If the client has made too much invalid request(e.g. request a non-exist data), - // reject them to prevent SPAM attack. - if atomic.LoadUint32(&p.invalidCount) > maxRequestErrors { - return errTooManyInvalidRequest - } - return nil -} - -// getAccount retrieves an account from the state based at root. -func (pm *ProtocolManager) getAccount(triedb *trie.Database, root, hash common.Hash) (state.Account, error) { - trie, err := trie.New(root, triedb) - if err != nil { - return state.Account{}, err - } - blob, err := trie.TryGet(hash[:]) - if err != nil { - return state.Account{}, err - } - var account state.Account - if err = rlp.DecodeBytes(blob, &account); err != nil { - return state.Account{}, err - } - return account, nil -} - -// getHelperTrie returns the post-processed trie root for the given trie ID and section index -func (pm *ProtocolManager) getHelperTrie(id uint, idx uint64) (common.Hash, string) { - switch id { - case htCanonical: - sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, (idx+1)*pm.iConfig.ChtSize-1) - return light.GetChtRoot(pm.chainDb, idx, sectionHead), light.ChtTablePrefix - case htBloomBits: - sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, (idx+1)*pm.iConfig.BloomTrieSize-1) - return light.GetBloomTrieRoot(pm.chainDb, idx, sectionHead), light.BloomTrieTablePrefix - } - return common.Hash{}, "" -} - -// getHelperTrieAuxData returns requested auxiliary data for the given HelperTrie request -func (pm *ProtocolManager) getHelperTrieAuxData(req HelperTrieReq) []byte { - if req.Type == htCanonical && req.AuxReq == auxHeader && len(req.Key) == 8 { - blockNum := binary.BigEndian.Uint64(req.Key) - hash := rawdb.ReadCanonicalHash(pm.chainDb, blockNum) - return rawdb.ReadHeaderRLP(pm.chainDb, hash, blockNum) - } - return nil -} - -func (pm *ProtocolManager) txStatus(hash common.Hash) light.TxStatus { - var stat light.TxStatus - stat.Status = pm.txpool.Status([]common.Hash{hash})[0] - // If the transaction is unknown to the pool, try looking it up locally - if stat.Status == core.TxStatusUnknown { - if tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(pm.chainDb, hash); tx != nil { - stat.Status = core.TxStatusIncluded - stat.Lookup = &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex} - } - } - return stat -} - -// downloaderPeerNotify implements peerSetNotify -type downloaderPeerNotify ProtocolManager - -type peerConnection struct { - manager *ProtocolManager - peer *peer -} - -func (pc *peerConnection) Head() (common.Hash, *big.Int) { - return pc.peer.HeadAndTd() -} - -func (pc *peerConnection) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { - reqID := genReqID() - rq := &distReq{ - getCost: func(dp distPeer) uint64 { - peer := dp.(*peer) - return peer.GetRequestCost(GetBlockHeadersMsg, amount) - }, - canSend: func(dp distPeer) bool { - return dp.(*peer) == pc.peer - }, - request: func(dp distPeer) func() { - peer := dp.(*peer) - cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) - peer.fcServer.QueuedRequest(reqID, cost) - return func() { peer.RequestHeadersByHash(reqID, cost, origin, amount, skip, reverse) } - }, - } - _, ok := <-pc.manager.reqDist.queue(rq) - if !ok { - return light.ErrNoPeers - } - return nil -} - -func (pc *peerConnection) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { - reqID := genReqID() - rq := &distReq{ - getCost: func(dp distPeer) uint64 { - peer := dp.(*peer) - return peer.GetRequestCost(GetBlockHeadersMsg, amount) - }, - canSend: func(dp distPeer) bool { - return dp.(*peer) == pc.peer - }, - request: func(dp distPeer) func() { - peer := dp.(*peer) - cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) - peer.fcServer.QueuedRequest(reqID, cost) - return func() { peer.RequestHeadersByNumber(reqID, cost, origin, amount, skip, reverse) } - }, - } - _, ok := <-pc.manager.reqDist.queue(rq) - if !ok { - return light.ErrNoPeers - } - return nil -} - -func (d *downloaderPeerNotify) registerPeer(p *peer) { - pm := (*ProtocolManager)(d) - pc := &peerConnection{ - manager: pm, - peer: p, - } - pm.downloader.RegisterLightPeer(p.id, ethVersion, pc) -} - -func (d *downloaderPeerNotify) unregisterPeer(p *peer) { - pm := (*ProtocolManager)(d) - pm.downloader.UnregisterPeer(p.id) -} diff --git a/vendor/github.com/ethereum/go-ethereum/les/metrics.go b/vendor/github.com/ethereum/go-ethereum/les/metrics.go index 4fe7031163..9ef8c36518 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/metrics.go +++ b/vendor/github.com/ethereum/go-ethereum/les/metrics.go @@ -22,31 +22,91 @@ import ( ) var ( - miscInPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets", nil) - miscInTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic", nil) - miscOutPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets", nil) - miscOutTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic", nil) - - connectionTimer = metrics.NewRegisteredTimer("les/connectionTime", nil) - - totalConnectedGauge = metrics.NewRegisteredGauge("les/server/totalConnected", nil) - totalCapacityGauge = metrics.NewRegisteredGauge("les/server/totalCapacity", nil) - totalRechargeGauge = metrics.NewRegisteredGauge("les/server/totalRecharge", nil) - blockProcessingTimer = metrics.NewRegisteredTimer("les/server/blockProcessingTime", nil) - requestServedTimer = metrics.NewRegisteredTimer("les/server/requestServed", nil) - requestServedMeter = metrics.NewRegisteredMeter("les/server/totalRequestServed", nil) - requestEstimatedMeter = metrics.NewRegisteredMeter("les/server/totalRequestEstimated", nil) - relativeCostHistogram = metrics.NewRegisteredHistogram("les/server/relativeCost", nil, metrics.NewExpDecaySample(1028, 0.015)) - recentServedGauge = metrics.NewRegisteredGauge("les/server/recentRequestServed", nil) - recentEstimatedGauge = metrics.NewRegisteredGauge("les/server/recentRequestEstimated", nil) - sqServedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/served", nil) - sqQueuedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/queued", nil) + miscInPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/total", nil) + miscInTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/total", nil) + miscInHeaderPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/header", nil) + miscInHeaderTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/header", nil) + miscInBodyPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/body", nil) + miscInBodyTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/body", nil) + miscInCodePacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/code", nil) + miscInCodeTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/code", nil) + miscInReceiptPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/receipt", nil) + miscInReceiptTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/receipt", nil) + miscInTrieProofPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/proof", nil) + miscInTrieProofTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/proof", nil) + miscInHelperTriePacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/helperTrie", nil) + miscInHelperTrieTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/helperTrie", nil) + miscInTxsPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/txs", nil) + miscInTxsTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/txs", nil) + miscInTxStatusPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets/txStatus", nil) + miscInTxStatusTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic/txStatus", nil) + + miscOutPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/total", nil) + miscOutTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/total", nil) + miscOutHeaderPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/header", nil) + miscOutHeaderTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/header", nil) + miscOutBodyPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/body", nil) + miscOutBodyTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/body", nil) + miscOutCodePacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/code", nil) + miscOutCodeTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/code", nil) + miscOutReceiptPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/receipt", nil) + miscOutReceiptTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/receipt", nil) + miscOutTrieProofPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/proof", nil) + miscOutTrieProofTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/proof", nil) + miscOutHelperTriePacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/helperTrie", nil) + miscOutHelperTrieTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/helperTrie", nil) + miscOutTxsPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/txs", nil) + miscOutTxsTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/txs", nil) + miscOutTxStatusPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets/txStatus", nil) + miscOutTxStatusTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic/txStatus", nil) + + miscServingTimeHeaderTimer = metrics.NewRegisteredTimer("les/misc/serve/header", nil) + miscServingTimeBodyTimer = metrics.NewRegisteredTimer("les/misc/serve/body", nil) + miscServingTimeCodeTimer = metrics.NewRegisteredTimer("les/misc/serve/code", nil) + miscServingTimeReceiptTimer = metrics.NewRegisteredTimer("les/misc/serve/receipt", nil) + miscServingTimeTrieProofTimer = metrics.NewRegisteredTimer("les/misc/serve/proof", nil) + miscServingTimeHelperTrieTimer = metrics.NewRegisteredTimer("les/misc/serve/helperTrie", nil) + miscServingTimeTxTimer = metrics.NewRegisteredTimer("les/misc/serve/txs", nil) + miscServingTimeTxStatusTimer = metrics.NewRegisteredTimer("les/misc/serve/txStatus", nil) + + connectionTimer = metrics.NewRegisteredTimer("les/connection/duration", nil) + serverConnectionGauge = metrics.NewRegisteredGauge("les/connection/server", nil) + clientConnectionGauge = metrics.NewRegisteredGauge("les/connection/client", nil) + + totalCapacityGauge = metrics.NewRegisteredGauge("les/server/totalCapacity", nil) + totalRechargeGauge = metrics.NewRegisteredGauge("les/server/totalRecharge", nil) + totalConnectedGauge = metrics.NewRegisteredGauge("les/server/totalConnected", nil) + blockProcessingTimer = metrics.NewRegisteredTimer("les/server/blockProcessingTime", nil) + + requestServedMeter = metrics.NewRegisteredMeter("les/server/req/avgServedTime", nil) + requestServedTimer = metrics.NewRegisteredTimer("les/server/req/servedTime", nil) + requestEstimatedMeter = metrics.NewRegisteredMeter("les/server/req/avgEstimatedTime", nil) + requestEstimatedTimer = metrics.NewRegisteredTimer("les/server/req/estimatedTime", nil) + relativeCostHistogram = metrics.NewRegisteredHistogram("les/server/req/relative", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostHeaderHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/header", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostBodyHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/body", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostReceiptHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/receipt", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostCodeHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/code", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostProofHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/proof", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostHelperProofHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/helperTrie", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostSendTxHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/txs", nil, metrics.NewExpDecaySample(1028, 0.015)) + relativeCostTxStatusHistogram = metrics.NewRegisteredHistogram("les/server/req/relative/txStatus", nil, metrics.NewExpDecaySample(1028, 0.015)) + + globalFactorGauge = metrics.NewRegisteredGauge("les/server/globalFactor", nil) + recentServedGauge = metrics.NewRegisteredGauge("les/server/recentRequestServed", nil) + recentEstimatedGauge = metrics.NewRegisteredGauge("les/server/recentRequestEstimated", nil) + sqServedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/served", nil) + sqQueuedGauge = metrics.NewRegisteredGauge("les/server/servingQueue/queued", nil) + clientConnectedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/connected", nil) clientRejectedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/rejected", nil) clientKickedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/kicked", nil) clientDisconnectedMeter = metrics.NewRegisteredMeter("les/server/clientEvent/disconnected", nil) clientFreezeMeter = metrics.NewRegisteredMeter("les/server/clientEvent/freeze", nil) clientErrorMeter = metrics.NewRegisteredMeter("les/server/clientEvent/error", nil) + + requestRTT = metrics.NewRegisteredTimer("les/client/req/rtt", nil) + requestSendDelay = metrics.NewRegisteredTimer("les/client/req/sendDelay", nil) ) // meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of @@ -58,17 +118,11 @@ type meteredMsgReadWriter struct { // newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the // metrics system is disabled, this function returns the original object. -func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter { +func newMeteredMsgWriter(rw p2p.MsgReadWriter, version int) p2p.MsgReadWriter { if !metrics.Enabled { return rw } - return &meteredMsgReadWriter{MsgReadWriter: rw} -} - -// Init sets the protocol version used by the stream to know which meters to -// increment in case of overlapping message ids between protocol versions. -func (rw *meteredMsgReadWriter) Init(version int) { - rw.version = version + return &meteredMsgReadWriter{MsgReadWriter: rw, version: version} } func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) { diff --git a/vendor/github.com/ethereum/go-ethereum/les/odr.go b/vendor/github.com/ethereum/go-ethereum/les/odr.go index a26c06680b..136ecf4df4 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/odr.go +++ b/vendor/github.com/ethereum/go-ethereum/les/odr.go @@ -18,7 +18,9 @@ package les import ( "context" + "time" + "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/light" @@ -120,10 +122,11 @@ func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err erro return func() { lreq.Request(reqID, p) } }, } - + sent := mclock.Now() if err = odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err == nil { // retrieved from network, store in db req.StoreResult(odr.db) + requestRTT.Update(time.Duration(mclock.Now() - sent)) } else { log.Debug("Failed to retrieve data from network", "err", err) } diff --git a/vendor/github.com/ethereum/go-ethereum/les/peer.go b/vendor/github.com/ethereum/go-ethereum/les/peer.go index bcd91cd835..ab5b30a657 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/les/peer.go @@ -94,6 +94,7 @@ type peer struct { sendQueue *execQueue errCh chan error + // responseLock ensures that responses are queued in the same order as // RequestProcessed is called responseLock sync.Mutex @@ -107,11 +108,10 @@ type peer struct { updateTime mclock.AbsTime frozen uint32 // 1 if client is in frozen state - fcClient *flowcontrol.ClientNode // nil if the peer is server only - fcServer *flowcontrol.ServerNode // nil if the peer is client only - fcParams flowcontrol.ServerParams - fcCosts requestCostTable - balanceTracker *balanceTracker // set by clientPool.connect, used and removed by ProtocolManager.handle + fcClient *flowcontrol.ClientNode // nil if the peer is server only + fcServer *flowcontrol.ServerNode // nil if the peer is client only + fcParams flowcontrol.ServerParams + fcCosts requestCostTable trusted bool onlyAnnounce bool @@ -291,6 +291,11 @@ func (p *peer) updateCapacity(cap uint64) { p.queueSend(func() { p.SendAnnounce(announceData{Update: kvList}) }) } +func (p *peer) responseID() uint64 { + p.responseCount += 1 + return p.responseCount +} + func sendRequest(w p2p.MsgWriter, msgcode, reqID, cost uint64, data interface{}) error { type req struct { ReqID uint64 @@ -373,6 +378,7 @@ func (p *peer) HasBlock(hash common.Hash, number uint64, hasState bool) bool { } hasBlock := p.hasBlock p.lock.RUnlock() + return head >= number && number >= since && (recent == 0 || number+recent+4 > head) && hasBlock != nil && hasBlock(hash, number, hasState) } @@ -571,6 +577,8 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis defer p.lock.Unlock() var send keyValueList + + // Add some basic handshake fields send = send.add("protocolVersion", uint64(p.version)) send = send.add("networkId", p.network) send = send.add("headTd", td) @@ -578,7 +586,8 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis send = send.add("headNum", headNum) send = send.add("genesisHash", genesis) if server != nil { - if !server.onlyAnnounce { + // Add some information which services server can offer. + if !server.config.UltraLightOnlyAnnounce { send = send.add("serveHeaders", nil) send = send.add("serveChainSince", uint64(0)) send = send.add("serveStateSince", uint64(0)) @@ -594,25 +603,28 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis } send = send.add("flowControl/BL", server.defParams.BufLimit) send = send.add("flowControl/MRR", server.defParams.MinRecharge) + var costList RequestCostList - if server.costTracker != nil { - costList = server.costTracker.makeCostList(server.costTracker.globalFactor()) + if server.costTracker.testCostList != nil { + costList = server.costTracker.testCostList } else { - costList = testCostList(server.testCost) + costList = server.costTracker.makeCostList(server.costTracker.globalFactor()) } send = send.add("flowControl/MRC", costList) p.fcCosts = costList.decode(ProtocolLengths[uint(p.version)]) p.fcParams = server.defParams - if server.protocolManager != nil && server.protocolManager.reg != nil && server.protocolManager.reg.isRunning() { - cp, height := server.protocolManager.reg.stableCheckpoint() + // Add advertised checkpoint and register block height which + // client can verify the checkpoint validity. + if server.oracle != nil && server.oracle.isRunning() { + cp, height := server.oracle.stableCheckpoint() if cp != nil { send = send.add("checkpoint/value", cp) send = send.add("checkpoint/registerHeight", height) } } } else { - //on client node + // Add some client-specific handshake fields p.announceType = announceTypeSimple if p.trusted { p.announceType = announceTypeSigned @@ -663,17 +675,12 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis } if server != nil { - // until we have a proper peer connectivity API, allow LES connection to other servers - /*if recv.get("serveStateSince", nil) == nil { - return errResp(ErrUselessPeer, "wanted client, got server") - }*/ if recv.get("announceType", &p.announceType) != nil { - //set default announceType on server side + // set default announceType on server side p.announceType = announceTypeSimple } p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams) } else { - //mark OnlyAnnounce server if "serveHeaders", "serveChainSince", "serveStateSince" or "txRelay" fields don't exist if recv.get("serveChainSince", &p.chainSince) != nil { p.onlyAnnounce = true } @@ -730,15 +737,10 @@ func (p *peer) updateFlowControl(update keyValueMap) { if p.fcServer == nil { return } - params := p.fcParams - updateParams := false - if update.get("flowControl/BL", ¶ms.BufLimit) == nil { - updateParams = true - } - if update.get("flowControl/MRR", ¶ms.MinRecharge) == nil { - updateParams = true - } - if updateParams { + // If any of the flow control params is nil, refuse to update. + var params flowcontrol.ServerParams + if update.get("flowControl/BL", ¶ms.BufLimit) == nil && update.get("flowControl/MRR", ¶ms.MinRecharge) == nil { + // todo can light client set a minimal acceptable flow control params? p.fcParams = params p.fcServer.UpdateParams(params) } diff --git a/vendor/github.com/ethereum/go-ethereum/les/server.go b/vendor/github.com/ethereum/go-ethereum/les/server.go index 97e82a42b2..997a24191b 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/server.go +++ b/vendor/github.com/ethereum/go-ethereum/les/server.go @@ -18,15 +18,11 @@ package les import ( "crypto/ecdsa" - "sync" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" @@ -34,84 +30,98 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) -const bufLimitRatio = 6000 // fixed bufLimit/MRR ratio - type LesServer struct { lesCommons archiveMode bool // Flag whether the ethereum node runs in archive mode. + handler *serverHandler + lesTopics []discv5.Topic + privateKey *ecdsa.PrivateKey - fcManager *flowcontrol.ClientManager // nil if our node is client only + // Flow control and capacity management + fcManager *flowcontrol.ClientManager costTracker *costTracker - testCost uint64 defParams flowcontrol.ServerParams - lesTopics []discv5.Topic - privateKey *ecdsa.PrivateKey - quitSync chan struct{} - onlyAnnounce bool - - thcNormal, thcBlockProcessing int // serving thread count for normal operation and block processing mode + servingQueue *servingQueue + clientPool *clientPool - maxPeers int - minCapacity, maxCapacity, freeClientCap uint64 - clientPool *clientPool + freeCapacity uint64 // The minimal client capacity used for free client. + threadsIdle int // Request serving threads count when system is idle. + threadsBusy int // Request serving threads count when system is busy(block insertion). } func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) { + // Collect les protocol version information supported by local node. lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) for i, pv := range AdvertiseProtocolVersions { lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv) } - quitSync := make(chan struct{}) + // Calculate the number of threads used to service the light client + // requests based on the user-specified value. + threads := config.LightServ * 4 / 100 + if threads < 4 { + threads = 4 + } srv := &LesServer{ lesCommons: lesCommons{ + genesis: e.BlockChain().Genesis().Hash(), config: config, + chainConfig: e.BlockChain().Config(), iConfig: light.DefaultServerIndexerConfig, chainDb: e.ChainDb(), + peers: newPeerSet(), + chainReader: e.BlockChain(), chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations), bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency), + closeCh: make(chan struct{}), }, archiveMode: e.ArchiveMode(), - quitSync: quitSync, lesTopics: lesTopics, - onlyAnnounce: config.UltraLightOnlyAnnounce, + fcManager: flowcontrol.NewClientManager(nil, &mclock.System{}), + servingQueue: newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100), + threadsBusy: config.LightServ/100 + 1, + threadsIdle: threads, + } + srv.handler = newServerHandler(srv, e.BlockChain(), e.ChainDb(), e.TxPool(), e.Synced) + srv.costTracker, srv.freeCapacity = newCostTracker(e.ChainDb(), config) + + // Set up checkpoint oracle. + oracle := config.CheckpointOracle + if oracle == nil { + oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()] } - srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config) + srv.oracle = newCheckpointOracle(oracle, srv.localCheckpoint) - logger := log.New() - srv.thcNormal = config.LightServ * 4 / 100 - if srv.thcNormal < 4 { - srv.thcNormal = 4 + // Initialize server capacity management fields. + srv.defParams = flowcontrol.ServerParams{ + BufLimit: srv.freeCapacity * bufLimitRatio, + MinRecharge: srv.freeCapacity, } - srv.thcBlockProcessing = config.LightServ/100 + 1 - srv.fcManager = flowcontrol.NewClientManager(nil, &mclock.System{}) + // LES flow control tries to more or less guarantee the possibility for the + // clients to send a certain amount of requests at any time and get a quick + // response. Most of the clients want this guarantee but don't actually need + // to send requests most of the time. Our goal is to serve as many clients as + // possible while the actually used server capacity does not exceed the limits + totalRecharge := srv.costTracker.totalRecharge() + maxCapacity := srv.freeCapacity * uint64(srv.config.LightPeers) + if totalRecharge > maxCapacity { + maxCapacity = totalRecharge + } + srv.fcManager.SetCapacityLimits(srv.freeCapacity, maxCapacity, srv.freeCapacity*2) + srv.clientPool = newClientPool(srv.chainDb, srv.freeCapacity, mclock.System{}, func(id enode.ID) { go srv.peers.Unregister(peerIdToString(id)) }) + srv.clientPool.setPriceFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1}) checkpoint := srv.latestLocalCheckpoint() if !checkpoint.Empty() { - logger.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead, + log.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead, "chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot) } - srv.chtIndexer.Start(e.BlockChain()) - - oracle := config.CheckpointOracle - if oracle == nil { - oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()] - } - registrar := newCheckpointOracle(oracle, srv.getLocalCheckpoint) - // TODO(rjl493456442) Checkpoint is useless for les server, separate handler for client and server. - pm, err := NewProtocolManager(e.BlockChain().Config(), nil, light.DefaultServerIndexerConfig, config.UltraLightServers, config.UltraLightFraction, false, config.NetworkId, e.EventMux(), newPeerSet(), e.BlockChain(), e.TxPool(), e.ChainDb(), nil, nil, registrar, quitSync, new(sync.WaitGroup), e.Synced) - if err != nil { - return nil, err - } - srv.protocolManager = pm - pm.servingQueue = newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100) - pm.server = srv - return srv, nil } @@ -120,102 +130,34 @@ func (s *LesServer) APIs() []rpc.API { { Namespace: "les", Version: "1.0", - Service: NewPrivateLightAPI(&s.lesCommons, s.protocolManager.reg), + Service: NewPrivateLightAPI(&s.lesCommons), Public: false, }, } } -// startEventLoop starts an event handler loop that updates the recharge curve of -// the client manager and adjusts the client pool's size according to the total -// capacity updates coming from the client manager -func (s *LesServer) startEventLoop() { - s.protocolManager.wg.Add(1) - - var ( - processing, procLast bool - procStarted time.Time - ) - blockProcFeed := make(chan bool, 100) - s.protocolManager.blockchain.(*core.BlockChain).SubscribeBlockProcessingEvent(blockProcFeed) - totalRechargeCh := make(chan uint64, 100) - totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) - totalCapacityCh := make(chan uint64, 100) - updateRecharge := func() { - if processing { - if !procLast { - procStarted = time.Now() - } - s.protocolManager.servingQueue.setThreads(s.thcBlockProcessing) - s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) - } else { - if procLast { - blockProcessingTimer.UpdateSince(procStarted) - } - s.protocolManager.servingQueue.setThreads(s.thcNormal) - s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 16, totalRecharge / 2}, {totalRecharge / 2, totalRecharge / 2}, {totalRecharge, totalRecharge}}) +func (s *LesServer) Protocols() []p2p.Protocol { + ps := s.makeProtocols(ServerProtocolVersions, s.handler.runPeer, func(id enode.ID) interface{} { + if p := s.peers.Peer(peerIdToString(id)); p != nil { + return p.Info() } - procLast = processing + return nil + }) + // Add "les" ENR entries. + for i := range ps { + ps[i].Attributes = []enr.Entry{&lesEntry{}} } - updateRecharge() - totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) - s.clientPool.setLimits(s.maxPeers, totalCapacity) - - var maxFreePeers uint64 - go func() { - for { - select { - case processing = <-blockProcFeed: - updateRecharge() - case totalRecharge = <-totalRechargeCh: - updateRecharge() - case totalCapacity = <-totalCapacityCh: - totalCapacityGauge.Update(int64(totalCapacity)) - newFreePeers := totalCapacity / s.freeClientCap - if newFreePeers < maxFreePeers && newFreePeers < uint64(s.maxPeers) { - log.Warn("Reduced total capacity", "maxFreePeers", newFreePeers) - } - maxFreePeers = newFreePeers - s.clientPool.setLimits(s.maxPeers, totalCapacity) - case <-s.protocolManager.quitSync: - s.protocolManager.wg.Done() - return - } - } - }() -} - -func (s *LesServer) Protocols() []p2p.Protocol { - return s.makeProtocols(ServerProtocolVersions) + return ps } // Start starts the LES server func (s *LesServer) Start(srvr *p2p.Server) { - s.maxPeers = s.config.LightPeers - totalRecharge := s.costTracker.totalRecharge() - if s.maxPeers > 0 { - s.freeClientCap = s.minCapacity //totalRecharge / uint64(s.maxPeers) - if s.freeClientCap < s.minCapacity { - s.freeClientCap = s.minCapacity - } - if s.freeClientCap > 0 { - s.defParams = flowcontrol.ServerParams{ - BufLimit: s.freeClientCap * bufLimitRatio, - MinRecharge: s.freeClientCap, - } - } - } + s.privateKey = srvr.PrivateKey + s.handler.start() + + s.wg.Add(1) + go s.capacityManagement() - s.maxCapacity = s.freeClientCap * uint64(s.maxPeers) - if totalRecharge > s.maxCapacity { - s.maxCapacity = totalRecharge - } - s.fcManager.SetCapacityLimits(s.freeClientCap, s.maxCapacity, s.freeClientCap*2) - s.clientPool = newClientPool(s.chainDb, s.freeClientCap, 10000, mclock.System{}, func(id enode.ID) { go s.protocolManager.removePeer(peerIdToString(id)) }) - s.clientPool.setPriceFactors(priceFactors{0, 1, 1}, priceFactors{0, 1, 1}) - s.protocolManager.peers.notify(s.clientPool) - s.startEventLoop() - s.protocolManager.Start(s.config.LightPeers) if srvr.DiscV5 != nil { for _, topic := range s.lesTopics { topic := topic @@ -224,12 +166,32 @@ func (s *LesServer) Start(srvr *p2p.Server) { logger.Info("Starting topic registration") defer logger.Info("Terminated topic registration") - srvr.DiscV5.RegisterTopic(topic, s.quitSync) + srvr.DiscV5.RegisterTopic(topic, s.closeCh) }() } } - s.privateKey = srvr.PrivateKey - s.protocolManager.blockLoop() +} + +// Stop stops the LES service +func (s *LesServer) Stop() { + close(s.closeCh) + + // Disconnect existing sessions. + // This also closes the gate for any new registrations on the peer set. + // sessions which are already established but not added to pm.peers yet + // will exit when they try to register. + s.peers.Close() + + s.fcManager.Stop() + s.costTracker.stop() + s.handler.stop() + s.clientPool.stop() // client pool should be closed after handler. + s.servingQueue.stop() + + // Note, bloom trie indexer is closed by parent bloombits indexer. + s.chtIndexer.Close() + s.wg.Wait() + log.Info("Les server stopped") } func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) { @@ -238,78 +200,67 @@ func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) { // SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched. func (s *LesServer) SetContractBackend(backend bind.ContractBackend) { - if s.protocolManager.reg != nil { - s.protocolManager.reg.start(backend) + if s.oracle == nil { + return } + s.oracle.start(backend) } -// Stop stops the LES service -func (s *LesServer) Stop() { - s.fcManager.Stop() - s.chtIndexer.Close() - // bloom trie indexer is closed by parent bloombits indexer - go func() { - <-s.protocolManager.noMorePeers - }() - s.clientPool.stop() - s.costTracker.stop() - s.protocolManager.Stop() -} +// capacityManagement starts an event handler loop that updates the recharge curve of +// the client manager and adjusts the client pool's size according to the total +// capacity updates coming from the client manager +func (s *LesServer) capacityManagement() { + defer s.wg.Done() -// todo(rjl493456442) separate client and server implementation. -func (pm *ProtocolManager) blockLoop() { - pm.wg.Add(1) - headCh := make(chan core.ChainHeadEvent, 10) - headSub := pm.blockchain.SubscribeChainHeadEvent(headCh) - go func() { - var lastHead *types.Header - lastBroadcastTd := common.Big0 - for { - select { - case ev := <-headCh: - peers := pm.peers.AllPeers() - if len(peers) > 0 { - header := ev.Block.Header() - hash := header.Hash() - number := header.Number.Uint64() - td := rawdb.ReadTd(pm.chainDb, hash, number) - if td != nil && td.Cmp(lastBroadcastTd) > 0 { - var reorg uint64 - if lastHead != nil { - reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() - } - lastHead = header - lastBroadcastTd = td + processCh := make(chan bool, 100) + sub := s.handler.blockchain.SubscribeBlockProcessingEvent(processCh) + defer sub.Unsubscribe() - log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) + totalRechargeCh := make(chan uint64, 100) + totalRecharge := s.costTracker.subscribeTotalRecharge(totalRechargeCh) + + totalCapacityCh := make(chan uint64, 100) + totalCapacity := s.fcManager.SubscribeTotalCapacity(totalCapacityCh) + s.clientPool.setLimits(s.config.LightPeers, totalCapacity) - announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} - var ( - signed bool - signedAnnounce announceData - ) + var ( + busy bool + freePeers uint64 + blockProcess mclock.AbsTime + ) + updateRecharge := func() { + if busy { + s.servingQueue.setThreads(s.threadsBusy) + s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge, totalRecharge}}) + } else { + s.servingQueue.setThreads(s.threadsIdle) + s.fcManager.SetRechargeCurve(flowcontrol.PieceWiseLinear{{0, 0}, {totalRecharge / 10, totalRecharge}, {totalRecharge, totalRecharge}}) + } + } + updateRecharge() - for _, p := range peers { - p := p - switch p.announceType { - case announceTypeSimple: - p.queueSend(func() { p.SendAnnounce(announce) }) - case announceTypeSigned: - if !signed { - signedAnnounce = announce - signedAnnounce.sign(pm.server.privateKey) - signed = true - } - p.queueSend(func() { p.SendAnnounce(signedAnnounce) }) - } - } - } - } - case <-pm.quitSync: - headSub.Unsubscribe() - pm.wg.Done() - return + for { + select { + case busy = <-processCh: + if busy { + blockProcess = mclock.Now() + } else { + blockProcessingTimer.Update(time.Duration(mclock.Now() - blockProcess)) } + updateRecharge() + case totalRecharge = <-totalRechargeCh: + totalRechargeGauge.Update(int64(totalRecharge)) + updateRecharge() + case totalCapacity = <-totalCapacityCh: + totalCapacityGauge.Update(int64(totalCapacity)) + newFreePeers := totalCapacity / s.freeCapacity + if newFreePeers < freePeers && newFreePeers < uint64(s.config.LightPeers) { + log.Warn("Reduced free peer connections", "from", freePeers, "to", newFreePeers) + } + freePeers = newFreePeers + s.clientPool.setLimits(s.config.LightPeers, totalCapacity) + case <-s.closeCh: + return } - }() + } } diff --git a/vendor/github.com/ethereum/go-ethereum/les/server_handler.go b/vendor/github.com/ethereum/go-ethereum/les/server_handler.go new file mode 100644 index 0000000000..16249ef1ba --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/server_handler.go @@ -0,0 +1,950 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package les + +import ( + "encoding/binary" + "encoding/json" + "errors" + "sync" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +const ( + softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. + estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header + ethVersion = 63 // equivalent eth version for the downloader + + MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request + MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request + MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request + MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request + MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request + MaxHelperTrieProofsFetch = 64 // Amount of helper tries to be fetched per retrieval request + MaxTxSend = 64 // Amount of transactions to be send per request + MaxTxStatus = 256 // Amount of transactions to queried per request +) + +var ( + errTooManyInvalidRequest = errors.New("too many invalid requests made") + errFullClientPool = errors.New("client pool is full") +) + +// serverHandler is responsible for serving light client and process +// all incoming light requests. +type serverHandler struct { + blockchain *core.BlockChain + chainDb ethdb.Database + txpool *core.TxPool + server *LesServer + + closeCh chan struct{} // Channel used to exit all background routines of handler. + wg sync.WaitGroup // WaitGroup used to track all background routines of handler. + synced func() bool // Callback function used to determine whether local node is synced. + + // Testing fields + addTxsSync bool +} + +func newServerHandler(server *LesServer, blockchain *core.BlockChain, chainDb ethdb.Database, txpool *core.TxPool, synced func() bool) *serverHandler { + handler := &serverHandler{ + server: server, + blockchain: blockchain, + chainDb: chainDb, + txpool: txpool, + closeCh: make(chan struct{}), + synced: synced, + } + return handler +} + +// start starts the server handler. +func (h *serverHandler) start() { + h.wg.Add(1) + go h.broadcastHeaders() +} + +// stop stops the server handler. +func (h *serverHandler) stop() { + close(h.closeCh) + h.wg.Wait() +} + +// runPeer is the p2p protocol run function for the given version. +func (h *serverHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter) error { + peer := newPeer(int(version), h.server.config.NetworkId, false, p, newMeteredMsgWriter(rw, int(version))) + h.wg.Add(1) + defer h.wg.Done() + return h.handle(peer) +} + +func (h *serverHandler) handle(p *peer) error { + // Reject light clients if server is not synced. + if !h.synced() { + return p2p.DiscRequested + } + p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) + + // Execute the LES handshake + var ( + head = h.blockchain.CurrentHeader() + hash = head.Hash() + number = head.Number.Uint64() + td = h.blockchain.GetTd(hash, number) + ) + if err := p.Handshake(td, hash, number, h.blockchain.Genesis().Hash(), h.server); err != nil { + p.Log().Debug("Light Ethereum handshake failed", "err", err) + return err + } + defer p.fcClient.Disconnect() + + // Disconnect the inbound peer if it's rejected by clientPool + if !h.server.clientPool.connect(p, 0) { + p.Log().Debug("Light Ethereum peer registration failed", "err", errFullClientPool) + return errFullClientPool + } + // Register the peer locally + if err := h.server.peers.Register(p); err != nil { + h.server.clientPool.disconnect(p) + p.Log().Error("Light Ethereum peer registration failed", "err", err) + return err + } + clientConnectionGauge.Update(int64(h.server.peers.Len())) + + var wg sync.WaitGroup // Wait group used to track all in-flight task routines. + + connectedAt := mclock.Now() + defer func() { + wg.Wait() // Ensure all background task routines have exited. + h.server.peers.Unregister(p.id) + h.server.clientPool.disconnect(p) + clientConnectionGauge.Update(int64(h.server.peers.Len())) + connectionTimer.Update(time.Duration(mclock.Now() - connectedAt)) + }() + + // Spawn a main loop to handle all incoming messages. + for { + select { + case err := <-p.errCh: + p.Log().Debug("Failed to send light ethereum response", "err", err) + return err + default: + } + if err := h.handleMsg(p, &wg); err != nil { + p.Log().Debug("Light Ethereum message handling failed", "err", err) + return err + } + } +} + +// handleMsg is invoked whenever an inbound message is received from a remote +// peer. The remote connection is torn down upon returning any error. +func (h *serverHandler) handleMsg(p *peer, wg *sync.WaitGroup) error { + // Read the next message from the remote peer, and ensure it's fully consumed + msg, err := p.rw.ReadMsg() + if err != nil { + return err + } + p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size) + + // Discard large message which exceeds the limitation. + if msg.Size > ProtocolMaxMsgSize { + clientErrorMeter.Mark(1) + return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) + } + defer msg.Discard() + + var ( + maxCost uint64 + task *servingTask + ) + p.responseCount++ + responseCount := p.responseCount + // accept returns an indicator whether the request can be served. + // If so, deduct the max cost from the flow control buffer. + accept := func(reqID, reqCnt, maxCnt uint64) bool { + // Short circuit if the peer is already frozen or the request is invalid. + inSizeCost := h.server.costTracker.realCost(0, msg.Size, 0) + if p.isFrozen() || reqCnt == 0 || reqCnt > maxCnt { + p.fcClient.OneTimeCost(inSizeCost) + return false + } + // Prepaid max cost units before request been serving. + maxCost = p.fcCosts.getMaxCost(msg.Code, reqCnt) + accepted, bufShort, priority := p.fcClient.AcceptRequest(reqID, responseCount, maxCost) + if !accepted { + p.freezeClient() + p.Log().Error("Request came too early", "remaining", common.PrettyDuration(time.Duration(bufShort*1000000/p.fcParams.MinRecharge))) + p.fcClient.OneTimeCost(inSizeCost) + return false + } + // Create a multi-stage task, estimate the time it takes for the task to + // execute, and cache it in the request service queue. + factor := h.server.costTracker.globalFactor() + if factor < 0.001 { + factor = 1 + p.Log().Error("Invalid global cost factor", "factor", factor) + } + maxTime := uint64(float64(maxCost) / factor) + task = h.server.servingQueue.newTask(p, maxTime, priority) + if task.start() { + return true + } + p.fcClient.RequestProcessed(reqID, responseCount, maxCost, inSizeCost) + return false + } + // sendResponse sends back the response and updates the flow control statistic. + sendResponse := func(reqID, amount uint64, reply *reply, servingTime uint64) { + p.responseLock.Lock() + defer p.responseLock.Unlock() + + // Short circuit if the client is already frozen. + if p.isFrozen() { + realCost := h.server.costTracker.realCost(servingTime, msg.Size, 0) + p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) + return + } + // Positive correction buffer value with real cost. + var replySize uint32 + if reply != nil { + replySize = reply.size() + } + var realCost uint64 + if h.server.costTracker.testing { + realCost = maxCost // Assign a fake cost for testing purpose + } else { + realCost = h.server.costTracker.realCost(servingTime, msg.Size, replySize) + } + bv := p.fcClient.RequestProcessed(reqID, responseCount, maxCost, realCost) + if amount != 0 { + // Feed cost tracker request serving statistic. + h.server.costTracker.updateStats(msg.Code, amount, servingTime, realCost) + // Reduce priority "balance" for the specific peer. + h.server.clientPool.requestCost(p, realCost) + } + if reply != nil { + p.queueSend(func() { + if err := reply.send(bv); err != nil { + select { + case p.errCh <- err: + default: + } + } + }) + } + } + switch msg.Code { + case GetBlockHeadersMsg: + p.Log().Trace("Received block header request") + if metrics.EnabledExpensive { + miscInHeaderPacketsMeter.Mark(1) + miscInHeaderTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeHeaderTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Query getBlockHeadersData + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "%v: %v", msg, err) + } + query := req.Query + if accept(req.ReqID, query.Amount, MaxHeaderFetch) { + wg.Add(1) + go func() { + defer wg.Done() + hashMode := query.Origin.Hash != (common.Hash{}) + first := true + maxNonCanonical := uint64(100) + + // Gather headers until the fetch or network limits is reached + var ( + bytes common.StorageSize + headers []*types.Header + unknown bool + ) + for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit { + if !first && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + // Retrieve the next header satisfying the query + var origin *types.Header + if hashMode { + if first { + origin = h.blockchain.GetHeaderByHash(query.Origin.Hash) + if origin != nil { + query.Origin.Number = origin.Number.Uint64() + } + } else { + origin = h.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number) + } + } else { + origin = h.blockchain.GetHeaderByNumber(query.Origin.Number) + } + if origin == nil { + atomic.AddUint32(&p.invalidCount, 1) + break + } + headers = append(headers, origin) + bytes += estHeaderRlpSize + + // Advance to the next header of the query + switch { + case hashMode && query.Reverse: + // Hash based traversal towards the genesis block + ancestor := query.Skip + 1 + if ancestor == 0 { + unknown = true + } else { + query.Origin.Hash, query.Origin.Number = h.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical) + unknown = query.Origin.Hash == common.Hash{} + } + case hashMode && !query.Reverse: + // Hash based traversal towards the leaf block + var ( + current = origin.Number.Uint64() + next = current + query.Skip + 1 + ) + if next <= current { + infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") + p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", query.Skip, "next", next, "attacker", infos) + unknown = true + } else { + if header := h.blockchain.GetHeaderByNumber(next); header != nil { + nextHash := header.Hash() + expOldHash, _ := h.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical) + if expOldHash == query.Origin.Hash { + query.Origin.Hash, query.Origin.Number = nextHash, next + } else { + unknown = true + } + } else { + unknown = true + } + } + case query.Reverse: + // Number based traversal towards the genesis block + if query.Origin.Number >= query.Skip+1 { + query.Origin.Number -= query.Skip + 1 + } else { + unknown = true + } + + case !query.Reverse: + // Number based traversal towards the leaf block + query.Origin.Number += query.Skip + 1 + } + first = false + } + reply := p.ReplyBlockHeaders(req.ReqID, headers) + sendResponse(req.ReqID, query.Amount, p.ReplyBlockHeaders(req.ReqID, headers), task.done()) + if metrics.EnabledExpensive { + miscOutHeaderPacketsMeter.Mark(1) + miscOutHeaderTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case GetBlockBodiesMsg: + p.Log().Trace("Received block bodies request") + if metrics.EnabledExpensive { + miscInBodyPacketsMeter.Mark(1) + miscInBodyTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeBodyTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Hashes []common.Hash + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + var ( + bytes int + bodies []rlp.RawValue + ) + reqCnt := len(req.Hashes) + if accept(req.ReqID, uint64(reqCnt), MaxBodyFetch) { + wg.Add(1) + go func() { + defer wg.Done() + for i, hash := range req.Hashes { + if i != 0 && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + if bytes >= softResponseLimit { + break + } + body := h.blockchain.GetBodyRLP(hash) + if body == nil { + atomic.AddUint32(&p.invalidCount, 1) + continue + } + bodies = append(bodies, body) + bytes += len(body) + } + reply := p.ReplyBlockBodiesRLP(req.ReqID, bodies) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutBodyPacketsMeter.Mark(1) + miscOutBodyTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case GetCodeMsg: + p.Log().Trace("Received code request") + if metrics.EnabledExpensive { + miscInCodePacketsMeter.Mark(1) + miscInCodeTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeCodeTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Reqs []CodeReq + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + var ( + bytes int + data [][]byte + ) + reqCnt := len(req.Reqs) + if accept(req.ReqID, uint64(reqCnt), MaxCodeFetch) { + wg.Add(1) + go func() { + defer wg.Done() + for i, request := range req.Reqs { + if i != 0 && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + // Look up the root hash belonging to the request + header := h.blockchain.GetHeaderByHash(request.BHash) + if header == nil { + p.Log().Warn("Failed to retrieve associate header for code", "hash", request.BHash) + atomic.AddUint32(&p.invalidCount, 1) + continue + } + // Refuse to search stale state data in the database since looking for + // a non-exist key is kind of expensive. + local := h.blockchain.CurrentHeader().Number.Uint64() + if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { + p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local) + atomic.AddUint32(&p.invalidCount, 1) + continue + } + triedb := h.blockchain.StateCache().TrieDB() + + account, err := h.getAccount(triedb, header.Root, common.BytesToHash(request.AccKey)) + if err != nil { + p.Log().Warn("Failed to retrieve account for code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) + atomic.AddUint32(&p.invalidCount, 1) + continue + } + code, err := triedb.Node(common.BytesToHash(account.CodeHash)) + if err != nil { + p.Log().Warn("Failed to retrieve account code", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "codehash", common.BytesToHash(account.CodeHash), "err", err) + continue + } + // Accumulate the code and abort if enough data was retrieved + data = append(data, code) + if bytes += len(code); bytes >= softResponseLimit { + break + } + } + reply := p.ReplyCode(req.ReqID, data) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutCodePacketsMeter.Mark(1) + miscOutCodeTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case GetReceiptsMsg: + p.Log().Trace("Received receipts request") + if metrics.EnabledExpensive { + miscInReceiptPacketsMeter.Mark(1) + miscInReceiptTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeReceiptTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Hashes []common.Hash + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + var ( + bytes int + receipts []rlp.RawValue + ) + reqCnt := len(req.Hashes) + if accept(req.ReqID, uint64(reqCnt), MaxReceiptFetch) { + wg.Add(1) + go func() { + defer wg.Done() + for i, hash := range req.Hashes { + if i != 0 && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + if bytes >= softResponseLimit { + break + } + // Retrieve the requested block's receipts, skipping if unknown to us + results := h.blockchain.GetReceiptsByHash(hash) + if results == nil { + if header := h.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { + atomic.AddUint32(&p.invalidCount, 1) + continue + } + } + // If known, encode and queue for response packet + if encoded, err := rlp.EncodeToBytes(results); err != nil { + log.Error("Failed to encode receipt", "err", err) + } else { + receipts = append(receipts, encoded) + bytes += len(encoded) + } + } + reply := p.ReplyReceiptsRLP(req.ReqID, receipts) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutReceiptPacketsMeter.Mark(1) + miscOutReceiptTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case GetProofsV2Msg: + p.Log().Trace("Received les/2 proofs request") + if metrics.EnabledExpensive { + miscInTrieProofPacketsMeter.Mark(1) + miscInTrieProofTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeTrieProofTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Reqs []ProofReq + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather state data until the fetch or network limits is reached + var ( + lastBHash common.Hash + root common.Hash + ) + reqCnt := len(req.Reqs) + if accept(req.ReqID, uint64(reqCnt), MaxProofsFetch) { + wg.Add(1) + go func() { + defer wg.Done() + nodes := light.NewNodeSet() + + for i, request := range req.Reqs { + if i != 0 && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + // Look up the root hash belonging to the request + var ( + header *types.Header + trie state.Trie + ) + if request.BHash != lastBHash { + root, lastBHash = common.Hash{}, request.BHash + + if header = h.blockchain.GetHeaderByHash(request.BHash); header == nil { + p.Log().Warn("Failed to retrieve header for proof", "hash", request.BHash) + atomic.AddUint32(&p.invalidCount, 1) + continue + } + // Refuse to search stale state data in the database since looking for + // a non-exist key is kind of expensive. + local := h.blockchain.CurrentHeader().Number.Uint64() + if !h.server.archiveMode && header.Number.Uint64()+core.TriesInMemory <= local { + p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local) + atomic.AddUint32(&p.invalidCount, 1) + continue + } + root = header.Root + } + // If a header lookup failed (non existent), ignore subsequent requests for the same header + if root == (common.Hash{}) { + atomic.AddUint32(&p.invalidCount, 1) + continue + } + // Open the account or storage trie for the request + statedb := h.blockchain.StateCache() + + switch len(request.AccKey) { + case 0: + // No account key specified, open an account trie + trie, err = statedb.OpenTrie(root) + if trie == nil || err != nil { + p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "root", root, "err", err) + continue + } + default: + // Account key specified, open a storage trie + account, err := h.getAccount(statedb.TrieDB(), root, common.BytesToHash(request.AccKey)) + if err != nil { + p.Log().Warn("Failed to retrieve account for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "err", err) + atomic.AddUint32(&p.invalidCount, 1) + continue + } + trie, err = statedb.OpenStorageTrie(common.BytesToHash(request.AccKey), account.Root) + if trie == nil || err != nil { + p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err) + continue + } + } + // Prove the user's request from the account or stroage trie + if err := trie.Prove(request.Key, request.FromLevel, nodes); err != nil { + p.Log().Warn("Failed to prove state request", "block", header.Number, "hash", header.Hash(), "err", err) + continue + } + if nodes.DataSize() >= softResponseLimit { + break + } + } + reply := p.ReplyProofsV2(req.ReqID, nodes.NodeList()) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutTrieProofPacketsMeter.Mark(1) + miscOutTrieProofTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case GetHelperTrieProofsMsg: + p.Log().Trace("Received helper trie proof request") + if metrics.EnabledExpensive { + miscInHelperTriePacketsMeter.Mark(1) + miscInHelperTrieTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeHelperTrieTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Reqs []HelperTrieReq + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + // Gather state data until the fetch or network limits is reached + var ( + auxBytes int + auxData [][]byte + ) + reqCnt := len(req.Reqs) + if accept(req.ReqID, uint64(reqCnt), MaxHelperTrieProofsFetch) { + wg.Add(1) + go func() { + defer wg.Done() + var ( + lastIdx uint64 + lastType uint + root common.Hash + auxTrie *trie.Trie + ) + nodes := light.NewNodeSet() + for i, request := range req.Reqs { + if i != 0 && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + if auxTrie == nil || request.Type != lastType || request.TrieIdx != lastIdx { + auxTrie, lastType, lastIdx = nil, request.Type, request.TrieIdx + + var prefix string + if root, prefix = h.getHelperTrie(request.Type, request.TrieIdx); root != (common.Hash{}) { + auxTrie, _ = trie.New(root, trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix))) + } + } + if request.AuxReq == auxRoot { + var data []byte + if root != (common.Hash{}) { + data = root[:] + } + auxData = append(auxData, data) + auxBytes += len(data) + } else { + if auxTrie != nil { + auxTrie.Prove(request.Key, request.FromLevel, nodes) + } + if request.AuxReq != 0 { + data := h.getAuxiliaryHeaders(request) + auxData = append(auxData, data) + auxBytes += len(data) + } + } + if nodes.DataSize()+auxBytes >= softResponseLimit { + break + } + } + reply := p.ReplyHelperTrieProofs(req.ReqID, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData}) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutHelperTriePacketsMeter.Mark(1) + miscOutHelperTrieTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case SendTxV2Msg: + p.Log().Trace("Received new transactions") + if metrics.EnabledExpensive { + miscInTxsPacketsMeter.Mark(1) + miscInTxsTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeTxTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Txs []*types.Transaction + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + reqCnt := len(req.Txs) + if accept(req.ReqID, uint64(reqCnt), MaxTxSend) { + wg.Add(1) + go func() { + defer wg.Done() + stats := make([]light.TxStatus, len(req.Txs)) + for i, tx := range req.Txs { + if i != 0 && !task.waitOrStop() { + return + } + hash := tx.Hash() + stats[i] = h.txStatus(hash) + if stats[i].Status == core.TxStatusUnknown { + addFn := h.txpool.AddRemotes + // Add txs synchronously for testing purpose + if h.addTxsSync { + addFn = h.txpool.AddRemotesSync + } + if errs := addFn([]*types.Transaction{tx}); errs[0] != nil { + stats[i].Error = errs[0].Error() + continue + } + stats[i] = h.txStatus(hash) + } + } + reply := p.ReplyTxStatus(req.ReqID, stats) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutTxsPacketsMeter.Mark(1) + miscOutTxsTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + case GetTxStatusMsg: + p.Log().Trace("Received transaction status query request") + if metrics.EnabledExpensive { + miscInTxStatusPacketsMeter.Mark(1) + miscInTxStatusTrafficMeter.Mark(int64(msg.Size)) + defer func(start time.Time) { miscServingTimeTxStatusTimer.UpdateSince(start) }(time.Now()) + } + var req struct { + ReqID uint64 + Hashes []common.Hash + } + if err := msg.Decode(&req); err != nil { + clientErrorMeter.Mark(1) + return errResp(ErrDecode, "msg %v: %v", msg, err) + } + reqCnt := len(req.Hashes) + if accept(req.ReqID, uint64(reqCnt), MaxTxStatus) { + wg.Add(1) + go func() { + defer wg.Done() + stats := make([]light.TxStatus, len(req.Hashes)) + for i, hash := range req.Hashes { + if i != 0 && !task.waitOrStop() { + sendResponse(req.ReqID, 0, nil, task.servingTime) + return + } + stats[i] = h.txStatus(hash) + } + reply := p.ReplyTxStatus(req.ReqID, stats) + sendResponse(req.ReqID, uint64(reqCnt), reply, task.done()) + if metrics.EnabledExpensive { + miscOutTxStatusPacketsMeter.Mark(1) + miscOutTxStatusTrafficMeter.Mark(int64(reply.size())) + } + }() + } + + default: + p.Log().Trace("Received invalid message", "code", msg.Code) + clientErrorMeter.Mark(1) + return errResp(ErrInvalidMsgCode, "%v", msg.Code) + } + // If the client has made too much invalid request(e.g. request a non-exist data), + // reject them to prevent SPAM attack. + if atomic.LoadUint32(&p.invalidCount) > maxRequestErrors { + clientErrorMeter.Mark(1) + return errTooManyInvalidRequest + } + return nil +} + +// getAccount retrieves an account from the state based on root. +func (h *serverHandler) getAccount(triedb *trie.Database, root, hash common.Hash) (state.Account, error) { + trie, err := trie.New(root, triedb) + if err != nil { + return state.Account{}, err + } + blob, err := trie.TryGet(hash[:]) + if err != nil { + return state.Account{}, err + } + var account state.Account + if err = rlp.DecodeBytes(blob, &account); err != nil { + return state.Account{}, err + } + return account, nil +} + +// getHelperTrie returns the post-processed trie root for the given trie ID and section index +func (h *serverHandler) getHelperTrie(typ uint, index uint64) (common.Hash, string) { + switch typ { + case htCanonical: + sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.ChtSize-1) + return light.GetChtRoot(h.chainDb, index, sectionHead), light.ChtTablePrefix + case htBloomBits: + sectionHead := rawdb.ReadCanonicalHash(h.chainDb, (index+1)*h.server.iConfig.BloomTrieSize-1) + return light.GetBloomTrieRoot(h.chainDb, index, sectionHead), light.BloomTrieTablePrefix + } + return common.Hash{}, "" +} + +// getAuxiliaryHeaders returns requested auxiliary headers for the CHT request. +func (h *serverHandler) getAuxiliaryHeaders(req HelperTrieReq) []byte { + if req.Type == htCanonical && req.AuxReq == auxHeader && len(req.Key) == 8 { + blockNum := binary.BigEndian.Uint64(req.Key) + hash := rawdb.ReadCanonicalHash(h.chainDb, blockNum) + return rawdb.ReadHeaderRLP(h.chainDb, hash, blockNum) + } + return nil +} + +// txStatus returns the status of a specified transaction. +func (h *serverHandler) txStatus(hash common.Hash) light.TxStatus { + var stat light.TxStatus + // Looking the transaction in txpool first. + stat.Status = h.txpool.Status([]common.Hash{hash})[0] + + // If the transaction is unknown to the pool, try looking it up locally. + if stat.Status == core.TxStatusUnknown { + lookup := h.blockchain.GetTransactionLookup(hash) + if lookup != nil { + stat.Status = core.TxStatusIncluded + stat.Lookup = lookup + } + } + return stat +} + +// broadcastHeaders broadcasts new block information to all connected light +// clients. According to the agreement between client and server, server should +// only broadcast new announcement if the total difficulty is higher than the +// last one. Besides server will add the signature if client requires. +func (h *serverHandler) broadcastHeaders() { + defer h.wg.Done() + + headCh := make(chan core.ChainHeadEvent, 10) + headSub := h.blockchain.SubscribeChainHeadEvent(headCh) + defer headSub.Unsubscribe() + + var ( + lastHead *types.Header + lastTd = common.Big0 + ) + for { + select { + case ev := <-headCh: + peers := h.server.peers.AllPeers() + if len(peers) == 0 { + continue + } + header := ev.Block.Header() + hash, number := header.Hash(), header.Number.Uint64() + td := h.blockchain.GetTd(hash, number) + if td == nil || td.Cmp(lastTd) <= 0 { + continue + } + var reorg uint64 + if lastHead != nil { + reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(h.chainDb, header, lastHead).Number.Uint64() + } + lastHead, lastTd = header, td + + log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) + var ( + signed bool + signedAnnounce announceData + ) + announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} + for _, p := range peers { + p := p + switch p.announceType { + case announceTypeSimple: + p.queueSend(func() { p.SendAnnounce(announce) }) + case announceTypeSigned: + if !signed { + signedAnnounce = announce + signedAnnounce.sign(h.server.privateKey) + signed = true + } + p.queueSend(func() { p.SendAnnounce(signedAnnounce) }) + } + } + case <-h.closeCh: + return + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/les/serverpool.go b/vendor/github.com/ethereum/go-ethereum/les/serverpool.go index 3e8cdee410..37621dc634 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/serverpool.go +++ b/vendor/github.com/ethereum/go-ethereum/les/serverpool.go @@ -115,8 +115,6 @@ type serverPool struct { db ethdb.Database dbKey []byte server *p2p.Server - quit chan struct{} - wg *sync.WaitGroup connWg sync.WaitGroup topic discv5.Topic @@ -137,14 +135,15 @@ type serverPool struct { connCh chan *connReq disconnCh chan *disconnReq registerCh chan *registerReq + + closeCh chan struct{} + wg sync.WaitGroup } // newServerPool creates a new serverPool instance -func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup, trustedNodes []string) *serverPool { +func newServerPool(db ethdb.Database, ulcServers []string) *serverPool { pool := &serverPool{ db: db, - quit: quit, - wg: wg, entries: make(map[enode.ID]*poolEntry), timeout: make(chan *poolEntry, 1), adjustStats: make(chan poolStatAdjust, 100), @@ -152,10 +151,11 @@ func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup, tr connCh: make(chan *connReq), disconnCh: make(chan *disconnReq), registerCh: make(chan *registerReq), + closeCh: make(chan struct{}), knownSelect: newWeightedRandomSelect(), newSelect: newWeightedRandomSelect(), fastDiscover: true, - trustedNodes: parseTrustedNodes(trustedNodes), + trustedNodes: parseTrustedNodes(ulcServers), } pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry) @@ -167,7 +167,6 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) { pool.server = server pool.topic = topic pool.dbKey = append([]byte("serverPool/"), []byte(topic)...) - pool.wg.Add(1) pool.loadNodes() pool.connectToTrustedNodes() @@ -178,9 +177,15 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) { go pool.discoverNodes() } pool.checkDial() + pool.wg.Add(1) go pool.eventLoop() } +func (pool *serverPool) stop() { + close(pool.closeCh) + pool.wg.Wait() +} + // discoverNodes wraps SearchTopic, converting result nodes to enode.Node. func (pool *serverPool) discoverNodes() { ch := make(chan *discv5.Node) @@ -207,7 +212,7 @@ func (pool *serverPool) connect(p *peer, node *enode.Node) *poolEntry { req := &connReq{p: p, node: node, result: make(chan *poolEntry, 1)} select { case pool.connCh <- req: - case <-pool.quit: + case <-pool.closeCh: return nil } return <-req.result @@ -219,7 +224,7 @@ func (pool *serverPool) registered(entry *poolEntry) { req := ®isterReq{entry: entry, done: make(chan struct{})} select { case pool.registerCh <- req: - case <-pool.quit: + case <-pool.closeCh: return } <-req.done @@ -231,7 +236,7 @@ func (pool *serverPool) registered(entry *poolEntry) { func (pool *serverPool) disconnect(entry *poolEntry) { stopped := false select { - case <-pool.quit: + case <-pool.closeCh: stopped = true default: } @@ -278,6 +283,7 @@ func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, // eventLoop handles pool events and mutex locking for all internal functions func (pool *serverPool) eventLoop() { + defer pool.wg.Done() lookupCnt := 0 var convTime mclock.AbsTime if pool.discSetPeriod != nil { @@ -361,7 +367,7 @@ func (pool *serverPool) eventLoop() { case req := <-pool.connCh: if pool.trustedNodes[req.p.ID()] != nil { // ignore trusted nodes - req.result <- nil + req.result <- &poolEntry{trusted: true} } else { // Handle peer connection requests. entry := pool.entries[req.p.ID()] @@ -389,6 +395,9 @@ func (pool *serverPool) eventLoop() { } case req := <-pool.registerCh: + if req.entry.trusted { + continue + } // Handle peer registration requests. entry := req.entry entry.state = psRegistered @@ -402,10 +411,13 @@ func (pool *serverPool) eventLoop() { close(req.done) case req := <-pool.disconnCh: + if req.entry.trusted { + continue + } // Handle peer disconnection requests. disconnect(req, req.stopped) - case <-pool.quit: + case <-pool.closeCh: if pool.discSetPeriod != nil { close(pool.discSetPeriod) } @@ -421,7 +433,6 @@ func (pool *serverPool) eventLoop() { disconnect(req, true) } pool.saveNodes() - pool.wg.Done() return } } @@ -549,10 +560,10 @@ func (pool *serverPool) setRetryDial(entry *poolEntry) { entry.delayedRetry = true go func() { select { - case <-pool.quit: + case <-pool.closeCh: case <-time.After(delay): select { - case <-pool.quit: + case <-pool.closeCh: case pool.enableRetry <- entry: } } @@ -618,10 +629,10 @@ func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) { go func() { pool.server.AddPeer(entry.node) select { - case <-pool.quit: + case <-pool.closeCh: case <-time.After(dialTimeout): select { - case <-pool.quit: + case <-pool.closeCh: case pool.timeout <- entry: } } @@ -662,14 +673,14 @@ type poolEntry struct { lastConnected, dialed *poolEntryAddress addrSelect weightedRandomSelect - lastDiscovered mclock.AbsTime - known, knownSelected bool - connectStats, delayStats poolStats - responseStats, timeoutStats poolStats - state int - regTime mclock.AbsTime - queueIdx int - removed bool + lastDiscovered mclock.AbsTime + known, knownSelected, trusted bool + connectStats, delayStats poolStats + responseStats, timeoutStats poolStats + state int + regTime mclock.AbsTime + queueIdx int + removed bool delayedRetry bool shortRetry int diff --git a/vendor/github.com/ethereum/go-ethereum/les/sync.go b/vendor/github.com/ethereum/go-ethereum/les/sync.go index 54fd81c2c2..1214fefcaf 100644 --- a/vendor/github.com/ethereum/go-ethereum/les/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/les/sync.go @@ -43,35 +43,6 @@ const ( checkpointSync ) -// syncer is responsible for periodically synchronising with the network, both -// downloading hashes and blocks as well as handling the announcement handler. -func (pm *ProtocolManager) syncer() { - // Start and ensure cleanup of sync mechanisms - //pm.fetcher.Start() - //defer pm.fetcher.Stop() - defer pm.downloader.Terminate() - - // Wait for different events to fire synchronisation operations - //forceSync := time.Tick(forceSyncCycle) - for { - select { - case <-pm.newPeerCh: - /* // Make sure we have peers to select from, then sync - if pm.peers.Len() < minDesiredPeerCount { - break - } - go pm.synchronise(pm.peers.BestPeer()) - */ - /*case <-forceSync: - // Force a sync even if not enough peers are present - go pm.synchronise(pm.peers.BestPeer()) - */ - case <-pm.noMorePeers: - return - } - } -} - // validateCheckpoint verifies the advertised checkpoint by peer is valid or not. // // Each network has several hard-coded checkpoint signer addresses. Only the @@ -80,22 +51,22 @@ func (pm *ProtocolManager) syncer() { // In addition to the checkpoint registered in the registrar contract, there are // several legacy hardcoded checkpoints in our codebase. These checkpoints are // also considered as valid. -func (pm *ProtocolManager) validateCheckpoint(peer *peer) error { +func (h *clientHandler) validateCheckpoint(peer *peer) error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() // Fetch the block header corresponding to the checkpoint registration. cp := peer.checkpoint - header, err := light.GetUntrustedHeaderByNumber(ctx, pm.odr, peer.checkpointNumber, peer.id) + header, err := light.GetUntrustedHeaderByNumber(ctx, h.backend.odr, peer.checkpointNumber, peer.id) if err != nil { return err } // Fetch block logs associated with the block header. - logs, err := light.GetUntrustedBlockLogs(ctx, pm.odr, header) + logs, err := light.GetUntrustedBlockLogs(ctx, h.backend.odr, header) if err != nil { return err } - events := pm.reg.contract.LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash()) + events := h.backend.oracle.contract.LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash()) if len(events) == 0 { return errInvalidCheckpoint } @@ -107,7 +78,7 @@ func (pm *ProtocolManager) validateCheckpoint(peer *peer) error { for _, event := range events { signatures = append(signatures, append(event.R[:], append(event.S[:], event.V)...)) } - valid, signers := pm.reg.verifySigners(index, hash, signatures) + valid, signers := h.backend.oracle.verifySigners(index, hash, signatures) if !valid { return errInvalidCheckpoint } @@ -116,14 +87,14 @@ func (pm *ProtocolManager) validateCheckpoint(peer *peer) error { } // synchronise tries to sync up our local chain with a remote peer. -func (pm *ProtocolManager) synchronise(peer *peer) { +func (h *clientHandler) synchronise(peer *peer) { // Short circuit if the peer is nil. if peer == nil { return } // Make sure the peer's TD is higher than our own. - latest := pm.blockchain.CurrentHeader() - currentTd := rawdb.ReadTd(pm.chainDb, latest.Hash(), latest.Number.Uint64()) + latest := h.backend.blockchain.CurrentHeader() + currentTd := rawdb.ReadTd(h.backend.chainDb, latest.Hash(), latest.Number.Uint64()) if currentTd != nil && peer.headBlockInfo().Td.Cmp(currentTd) < 0 { return } @@ -140,8 +111,8 @@ func (pm *ProtocolManager) synchronise(peer *peer) { // => Use provided checkpoint var checkpoint = &peer.checkpoint var hardcoded bool - if pm.checkpoint != nil && pm.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex { - checkpoint = pm.checkpoint // Use the hardcoded one. + if h.checkpoint != nil && h.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex { + checkpoint = h.checkpoint // Use the hardcoded one. hardcoded = true } // Determine whether we should run checkpoint syncing or normal light syncing. @@ -157,34 +128,37 @@ func (pm *ProtocolManager) synchronise(peer *peer) { case checkpoint.Empty(): mode = lightSync log.Debug("Disable checkpoint syncing", "reason", "empty checkpoint") - case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*pm.iConfig.ChtSize-1: + case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*h.backend.iConfig.ChtSize-1: mode = lightSync log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint") case hardcoded: mode = legacyCheckpointSync log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded") - case pm.reg == nil || !pm.reg.isRunning(): - mode = legacyCheckpointSync + case h.backend.oracle == nil || !h.backend.oracle.isRunning(): + if h.checkpoint == nil { + mode = lightSync // Downgrade to light sync unfortunately. + } else { + checkpoint = h.checkpoint + mode = legacyCheckpointSync + } log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated") } // Notify testing framework if syncing has completed(for testing purpose). defer func() { - if pm.reg != nil && pm.reg.syncDoneHook != nil { - pm.reg.syncDoneHook() + if h.syncDone != nil { + h.syncDone() } }() start := time.Now() if mode == checkpointSync || mode == legacyCheckpointSync { // Validate the advertised checkpoint - if mode == legacyCheckpointSync { - checkpoint = pm.checkpoint - } else if mode == checkpointSync { - if err := pm.validateCheckpoint(peer); err != nil { + if mode == checkpointSync { + if err := h.validateCheckpoint(peer); err != nil { log.Debug("Failed to validate checkpoint", "reason", err) - pm.removePeer(peer.id) + h.removePeer(peer.id) return } - pm.blockchain.(*light.LightChain).AddTrustedCheckpoint(checkpoint) + h.backend.blockchain.AddTrustedCheckpoint(checkpoint) } log.Debug("Checkpoint syncing start", "peer", peer.id, "checkpoint", checkpoint.SectionIndex) @@ -197,14 +171,14 @@ func (pm *ProtocolManager) synchronise(peer *peer) { // of the latest epoch covered by checkpoint. ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - if !checkpoint.Empty() && !pm.blockchain.(*light.LightChain).SyncCheckpoint(ctx, checkpoint) { + if !checkpoint.Empty() && !h.backend.blockchain.SyncCheckpoint(ctx, checkpoint) { log.Debug("Sync checkpoint failed") - pm.removePeer(peer.id) + h.removePeer(peer.id) return } } // Fetch the remaining block headers based on the current chain header. - if err := pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil { + if err := h.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil { log.Debug("Synchronise failed", "reason", err) return } diff --git a/vendor/github.com/ethereum/go-ethereum/les/test_helper.go b/vendor/github.com/ethereum/go-ethereum/les/test_helper.go new file mode 100644 index 0000000000..ee3d7a32e1 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/les/test_helper.go @@ -0,0 +1,558 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains some shares testing functionality, common to multiple +// different files and modules being tested. + +package les + +import ( + "context" + "crypto/rand" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/mclock" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/les/flowcontrol" + "github.com/ethereum/go-ethereum/light" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" +) + +var ( + bankKey, _ = crypto.GenerateKey() + bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey) + bankFunds = big.NewInt(1000000000000000000) + + userKey1, _ = crypto.GenerateKey() + userKey2, _ = crypto.GenerateKey() + userAddr1 = crypto.PubkeyToAddress(userKey1.PublicKey) + userAddr2 = crypto.PubkeyToAddress(userKey2.PublicKey) + + testContractAddr common.Address + testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") + testContractCodeDeployed = testContractCode[16:] + testContractDeployed = uint64(2) + + testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029") + + // Checkpoint registrar relative + registrarAddr common.Address + signerKey, _ = crypto.GenerateKey() + signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey) +) + +var ( + // The block frequency for creating checkpoint(only used in test) + sectionSize = big.NewInt(128) + + // The number of confirmations needed to generate a checkpoint(only used in test). + processConfirms = big.NewInt(1) + + // The token bucket buffer limit for testing purpose. + testBufLimit = uint64(1000000) + + // The buffer recharging speed for testing purpose. + testBufRecharge = uint64(1000) +) + +/* +contract test { + + uint256[100] data; + + function Put(uint256 addr, uint256 value) { + data[addr] = value; + } + + function Get(uint256 addr) constant returns (uint256 value) { + return data[addr]; + } +} +*/ + +// prepare pre-commits specified number customized blocks into chain. +func prepare(n int, backend *backends.SimulatedBackend) { + var ( + ctx = context.Background() + signer = types.HomesteadSigner{} + ) + for i := 0; i < n; i++ { + switch i { + case 0: + // deploy checkpoint contract + registrarAddr, _, _, _ = contract.DeployCheckpointOracle(bind.NewKeyedTransactor(bankKey), backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1)) + // bankUser transfers some ether to user1 + nonce, _ := backend.PendingNonceAt(ctx, bankAddr) + tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey) + backend.SendTransaction(ctx, tx) + case 1: + bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) + userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1) + + // bankUser transfers more ether to user1 + tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1000), params.TxGas, nil, nil), signer, bankKey) + backend.SendTransaction(ctx, tx1) + + // user1 relays ether to user2 + tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1000), params.TxGas, nil, nil), signer, userKey1) + backend.SendTransaction(ctx, tx2) + + // user1 deploys a test contract + tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, userKey1) + backend.SendTransaction(ctx, tx3) + testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1) + + // user1 deploys a event contract + tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, userKey1) + backend.SendTransaction(ctx, tx4) + case 2: + // bankUser transfer some ether to signer + bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) + tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, nil, nil), signer, bankKey) + backend.SendTransaction(ctx, tx1) + + // invoke test contract + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") + tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey) + backend.SendTransaction(ctx, tx2) + case 3: + // invoke test contract + bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr) + data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") + tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey) + backend.SendTransaction(ctx, tx) + } + backend.Commit() + } +} + +// testIndexers creates a set of indexers with specified params for testing purpose. +func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig) []*core.ChainIndexer { + var indexers [3]*core.ChainIndexer + indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms) + indexers[1] = eth.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms) + indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize) + // make bloomTrieIndexer as a child indexer of bloom indexer. + indexers[1].AddChildIndexer(indexers[2]) + return indexers[:] +} + +func newTestClientHandler(backend *backends.SimulatedBackend, odr *LesOdr, indexers []*core.ChainIndexer, db ethdb.Database, peers *peerSet, ulcServers []string, ulcFraction int) *clientHandler { + var ( + evmux = new(event.TypeMux) + engine = ethash.NewFaker() + gspec = core.Genesis{ + Config: params.AllEthashProtocolChanges, + Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, + GasLimit: 100000000, + } + oracle *checkpointOracle + ) + genesis := gspec.MustCommit(db) + chain, _ := light.NewLightChain(odr, gspec.Config, engine, nil) + if indexers != nil { + checkpointConfig := ¶ms.CheckpointOracleConfig{ + Address: crypto.CreateAddress(bankAddr, 0), + Signers: []common.Address{signerAddr}, + Threshold: 1, + } + getLocal := func(index uint64) params.TrustedCheckpoint { + chtIndexer := indexers[0] + sectionHead := chtIndexer.SectionHead(index) + return params.TrustedCheckpoint{ + SectionIndex: index, + SectionHead: sectionHead, + CHTRoot: light.GetChtRoot(db, index, sectionHead), + BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead), + } + } + oracle = newCheckpointOracle(checkpointConfig, getLocal) + } + client := &LightEthereum{ + lesCommons: lesCommons{ + genesis: genesis.Hash(), + config: ð.Config{LightPeers: 100, NetworkId: NetworkId}, + chainConfig: params.AllEthashProtocolChanges, + iConfig: light.TestClientIndexerConfig, + chainDb: db, + oracle: oracle, + chainReader: chain, + peers: peers, + closeCh: make(chan struct{}), + }, + reqDist: odr.retriever.dist, + retriever: odr.retriever, + odr: odr, + engine: engine, + blockchain: chain, + eventMux: evmux, + } + client.handler = newClientHandler(ulcServers, ulcFraction, nil, client) + + if client.oracle != nil { + client.oracle.start(backend) + } + return client.handler +} + +func newTestServerHandler(blocks int, indexers []*core.ChainIndexer, db ethdb.Database, peers *peerSet, clock mclock.Clock) (*serverHandler, *backends.SimulatedBackend) { + var ( + gspec = core.Genesis{ + Config: params.AllEthashProtocolChanges, + Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}}, + GasLimit: 100000000, + } + oracle *checkpointOracle + ) + genesis := gspec.MustCommit(db) + + // create a simulation backend and pre-commit several customized block to the database. + simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000) + prepare(blocks, simulation) + + txpoolConfig := core.DefaultTxPoolConfig + txpoolConfig.Journal = "" + txpool := core.NewTxPool(txpoolConfig, gspec.Config, simulation.Blockchain()) + if indexers != nil { + checkpointConfig := ¶ms.CheckpointOracleConfig{ + Address: crypto.CreateAddress(bankAddr, 0), + Signers: []common.Address{signerAddr}, + Threshold: 1, + } + getLocal := func(index uint64) params.TrustedCheckpoint { + chtIndexer := indexers[0] + sectionHead := chtIndexer.SectionHead(index) + return params.TrustedCheckpoint{ + SectionIndex: index, + SectionHead: sectionHead, + CHTRoot: light.GetChtRoot(db, index, sectionHead), + BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead), + } + } + oracle = newCheckpointOracle(checkpointConfig, getLocal) + } + server := &LesServer{ + lesCommons: lesCommons{ + genesis: genesis.Hash(), + config: ð.Config{LightPeers: 100, NetworkId: NetworkId}, + chainConfig: params.AllEthashProtocolChanges, + iConfig: light.TestServerIndexerConfig, + chainDb: db, + chainReader: simulation.Blockchain(), + oracle: oracle, + peers: peers, + closeCh: make(chan struct{}), + }, + servingQueue: newServingQueue(int64(time.Millisecond*10), 1), + defParams: flowcontrol.ServerParams{ + BufLimit: testBufLimit, + MinRecharge: testBufRecharge, + }, + fcManager: flowcontrol.NewClientManager(nil, clock), + } + server.costTracker, server.freeCapacity = newCostTracker(db, server.config) + server.costTracker.testCostList = testCostList(0) // Disable flow control mechanism. + server.clientPool = newClientPool(db, 1, clock, nil) + server.clientPool.setLimits(10000, 10000) // Assign enough capacity for clientpool + server.handler = newServerHandler(server, simulation.Blockchain(), db, txpool, func() bool { return true }) + if server.oracle != nil { + server.oracle.start(simulation) + } + server.servingQueue.setThreads(4) + server.handler.start() + return server.handler, simulation +} + +// testPeer is a simulated peer to allow testing direct network calls. +type testPeer struct { + peer *peer + + net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging + app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side +} + +// newTestPeer creates a new peer registered at the given protocol manager. +func newTestPeer(t *testing.T, name string, version int, handler *serverHandler, shake bool, testCost uint64) (*testPeer, <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Generate a random id and create the peer + var id enode.ID + rand.Read(id[:]) + peer := newPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net) + + // Start the peer on a new thread + errCh := make(chan error, 1) + go func() { + select { + case <-handler.closeCh: + errCh <- p2p.DiscQuitting + case errCh <- handler.handle(peer): + } + }() + tp := &testPeer{ + app: app, + net: net, + peer: peer, + } + // Execute any implicitly requested handshakes and return + if shake { + // Customize the cost table if required. + if testCost != 0 { + handler.server.costTracker.testCostList = testCostList(testCost) + } + var ( + genesis = handler.blockchain.Genesis() + head = handler.blockchain.CurrentHeader() + td = handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) + ) + tp.handshake(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), testCostList(testCost)) + } + return tp, errCh +} + +// close terminates the local side of the peer, notifying the remote protocol +// manager of termination. +func (p *testPeer) close() { + p.app.Close() +} + +func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler) (*testPeer, <-chan error, *testPeer, <-chan error) { + // Create a message pipe to communicate through + app, net := p2p.MsgPipe() + + // Generate a random id and create the peer + var id enode.ID + rand.Read(id[:]) + + peer1 := newPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), net) + peer2 := newPeer(version, NetworkId, false, p2p.NewPeer(id, name, nil), app) + + // Start the peer on a new thread + errc1 := make(chan error, 1) + errc2 := make(chan error, 1) + go func() { + select { + case <-server.closeCh: + errc1 <- p2p.DiscQuitting + case errc1 <- server.handle(peer1): + } + }() + go func() { + select { + case <-client.closeCh: + errc1 <- p2p.DiscQuitting + case errc1 <- client.handle(peer2): + } + }() + return &testPeer{peer: peer1, net: net, app: app}, errc1, &testPeer{peer: peer2, net: app, app: net}, errc2 +} + +// handshake simulates a trivial handshake that expects the same state from the +// remote side as we are simulating locally. +func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, costList RequestCostList) { + var expList keyValueList + expList = expList.add("protocolVersion", uint64(p.peer.version)) + expList = expList.add("networkId", uint64(NetworkId)) + expList = expList.add("headTd", td) + expList = expList.add("headHash", head) + expList = expList.add("headNum", headNum) + expList = expList.add("genesisHash", genesis) + sendList := make(keyValueList, len(expList)) + copy(sendList, expList) + expList = expList.add("serveHeaders", nil) + expList = expList.add("serveChainSince", uint64(0)) + expList = expList.add("serveStateSince", uint64(0)) + expList = expList.add("serveRecentState", uint64(core.TriesInMemory-4)) + expList = expList.add("txRelay", nil) + expList = expList.add("flowControl/BL", testBufLimit) + expList = expList.add("flowControl/MRR", testBufRecharge) + expList = expList.add("flowControl/MRC", costList) + + if err := p2p.ExpectMsg(p.app, StatusMsg, expList); err != nil { + t.Fatalf("status recv: %v", err) + } + if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { + t.Fatalf("status send: %v", err) + } + p.peer.fcParams = flowcontrol.ServerParams{ + BufLimit: testBufLimit, + MinRecharge: testBufRecharge, + } +} + +type indexerCallback func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) + +// testClient represents a client for testing with necessary auxiliary fields. +type testClient struct { + clock mclock.Clock + db ethdb.Database + peer *testPeer + handler *clientHandler + + chtIndexer *core.ChainIndexer + bloomIndexer *core.ChainIndexer + bloomTrieIndexer *core.ChainIndexer +} + +// testServer represents a server for testing with necessary auxiliary fields. +type testServer struct { + clock mclock.Clock + backend *backends.SimulatedBackend + db ethdb.Database + peer *testPeer + handler *serverHandler + + chtIndexer *core.ChainIndexer + bloomIndexer *core.ChainIndexer + bloomTrieIndexer *core.ChainIndexer +} + +func newServerEnv(t *testing.T, blocks int, protocol int, callback indexerCallback, simClock bool, newPeer bool, testCost uint64) (*testServer, func()) { + db := rawdb.NewMemoryDatabase() + indexers := testIndexers(db, nil, light.TestServerIndexerConfig) + + var clock mclock.Clock = &mclock.System{} + if simClock { + clock = &mclock.Simulated{} + } + handler, b := newTestServerHandler(blocks, indexers, db, newPeerSet(), clock) + + var peer *testPeer + if newPeer { + peer, _ = newTestPeer(t, "peer", protocol, handler, true, testCost) + } + + cIndexer, bIndexer, btIndexer := indexers[0], indexers[1], indexers[2] + cIndexer.Start(handler.blockchain) + bIndexer.Start(handler.blockchain) + + // Wait until indexers generate enough index data. + if callback != nil { + callback(cIndexer, bIndexer, btIndexer) + } + server := &testServer{ + clock: clock, + backend: b, + db: db, + peer: peer, + handler: handler, + chtIndexer: cIndexer, + bloomIndexer: bIndexer, + bloomTrieIndexer: btIndexer, + } + teardown := func() { + if newPeer { + peer.close() + b.Close() + } + cIndexer.Close() + bIndexer.Close() + } + return server, teardown +} + +func newClientServerEnv(t *testing.T, blocks int, protocol int, callback indexerCallback, ulcServers []string, ulcFraction int, simClock bool, connect bool) (*testServer, *testClient, func()) { + sdb, cdb := rawdb.NewMemoryDatabase(), rawdb.NewMemoryDatabase() + speers, cPeers := newPeerSet(), newPeerSet() + + var clock mclock.Clock = &mclock.System{} + if simClock { + clock = &mclock.Simulated{} + } + dist := newRequestDistributor(cPeers, clock) + rm := newRetrieveManager(cPeers, dist, nil) + odr := NewLesOdr(cdb, light.TestClientIndexerConfig, rm) + + sindexers := testIndexers(sdb, nil, light.TestServerIndexerConfig) + cIndexers := testIndexers(cdb, odr, light.TestClientIndexerConfig) + + scIndexer, sbIndexer, sbtIndexer := sindexers[0], sindexers[1], sindexers[2] + ccIndexer, cbIndexer, cbtIndexer := cIndexers[0], cIndexers[1], cIndexers[2] + odr.SetIndexers(ccIndexer, cbIndexer, cbtIndexer) + + server, b := newTestServerHandler(blocks, sindexers, sdb, speers, clock) + client := newTestClientHandler(b, odr, cIndexers, cdb, cPeers, ulcServers, ulcFraction) + + scIndexer.Start(server.blockchain) + sbIndexer.Start(server.blockchain) + ccIndexer.Start(client.backend.blockchain) + cbIndexer.Start(client.backend.blockchain) + + if callback != nil { + callback(scIndexer, sbIndexer, sbtIndexer) + } + var ( + speer, cpeer *testPeer + err1, err2 <-chan error + ) + if connect { + cpeer, err1, speer, err2 = newTestPeerPair("peer", protocol, server, client) + select { + case <-time.After(time.Millisecond * 300): + case err := <-err1: + t.Fatalf("peer 1 handshake error: %v", err) + case err := <-err2: + t.Fatalf("peer 2 handshake error: %v", err) + } + } + s := &testServer{ + clock: clock, + backend: b, + db: sdb, + peer: cpeer, + handler: server, + chtIndexer: scIndexer, + bloomIndexer: sbIndexer, + bloomTrieIndexer: sbtIndexer, + } + c := &testClient{ + clock: clock, + db: cdb, + peer: speer, + handler: client, + chtIndexer: ccIndexer, + bloomIndexer: cbIndexer, + bloomTrieIndexer: cbtIndexer, + } + teardown := func() { + if connect { + speer.close() + cpeer.close() + } + ccIndexer.Close() + cbIndexer.Close() + scIndexer.Close() + sbIndexer.Close() + b.Close() + } + return s, c, teardown +} diff --git a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go index 7f64d1c28b..02b90138a2 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/lightchain.go +++ b/vendor/github.com/ethereum/go-ethereum/light/lightchain.go @@ -426,6 +426,11 @@ func (lc *LightChain) HasHeader(hash common.Hash, number uint64) bool { return lc.hc.HasHeader(hash, number) } +// GetCanonicalHash returns the canonical hash for a given block number +func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash { + return bc.hc.GetCanonicalHash(number) +} + // GetBlockHashesFromHash retrieves a number of block hashes starting at a given // hash, fetching towards the genesis block. func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { @@ -438,9 +443,6 @@ func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com // // Note: ancestor == 0 returns the same block, 1 returns its parent and so on. func (lc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { - lc.chainmu.RLock() - defer lc.chainmu.RUnlock() - return lc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) } diff --git a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go index 82e33bb78f..2c820d40c7 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/odr_util.go +++ b/vendor/github.com/ethereum/go-ethereum/light/odr_util.go @@ -60,7 +60,7 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ } } if number >= chtCount*odr.IndexerConfig().ChtSize { - return nil, ErrNoTrustedCht + return nil, errNoTrustedCht } r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number, Config: odr.IndexerConfig()} if err := odr.Retrieve(ctx, r); err != nil { @@ -124,7 +124,7 @@ func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint // Retrieve the block header and body contents header := rawdb.ReadHeader(odr.Database(), hash, number) if header == nil { - return nil, ErrNoHeader + return nil, errNoHeader } body, err := GetBody(ctx, odr, hash, number) if err != nil { @@ -241,7 +241,7 @@ func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxLi } else { // TODO(rjl493456442) Convert sectionIndex to BloomTrie relative index if sectionIdx >= bloomTrieCount { - return nil, ErrNoTrustedBloomTrie + return nil, errNoTrustedBloomTrie } reqList = append(reqList, sectionIdx) reqIdx = append(reqIdx, i) diff --git a/vendor/github.com/ethereum/go-ethereum/light/postprocess.go b/vendor/github.com/ethereum/go-ethereum/light/postprocess.go index bf632a4496..af3b257923 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/postprocess.go +++ b/vendor/github.com/ethereum/go-ethereum/light/postprocess.go @@ -79,28 +79,28 @@ var ( } // TestServerIndexerConfig wraps a set of configs as a test indexer config for server side. TestServerIndexerConfig = &IndexerConfig{ - ChtSize: 512, - ChtConfirms: 4, - BloomSize: 64, - BloomConfirms: 4, - BloomTrieSize: 512, - BloomTrieConfirms: 4, + ChtSize: 128, + ChtConfirms: 1, + BloomSize: 16, + BloomConfirms: 1, + BloomTrieSize: 128, + BloomTrieConfirms: 1, } // TestClientIndexerConfig wraps a set of configs as a test indexer config for client side. TestClientIndexerConfig = &IndexerConfig{ - ChtSize: 512, - ChtConfirms: 32, - BloomSize: 512, - BloomConfirms: 32, - BloomTrieSize: 512, - BloomTrieConfirms: 32, + ChtSize: 128, + ChtConfirms: 8, + BloomSize: 128, + BloomConfirms: 8, + BloomTrieSize: 128, + BloomTrieConfirms: 8, } ) var ( - ErrNoTrustedCht = errors.New("no trusted canonical hash trie") - ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie") - ErrNoHeader = errors.New("header not found") + errNoTrustedCht = errors.New("no trusted canonical hash trie") + errNoTrustedBloomTrie = errors.New("no trusted bloom trie") + errNoHeader = errors.New("header not found") chtPrefix = []byte("chtRootV2-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash ChtTablePrefix = "cht-" ) diff --git a/vendor/github.com/ethereum/go-ethereum/light/txpool.go b/vendor/github.com/ethereum/go-ethereum/light/txpool.go index e945ef2ec1..11a0e76ae0 100644 --- a/vendor/github.com/ethereum/go-ethereum/light/txpool.go +++ b/vendor/github.com/ethereum/go-ethereum/light/txpool.go @@ -19,6 +19,7 @@ package light import ( "context" "fmt" + "math/big" "sync" "time" @@ -67,7 +68,7 @@ type TxPool struct { mined map[common.Hash][]*types.Transaction // mined transactions by block hash clearIdx uint64 // earliest block nr that can contain mined tx info - homestead bool + istanbul bool // Fork indicator whether we are in the istanbul stage. } // TxRelayBackend provides an interface to the mechanism that forwards transacions @@ -309,8 +310,10 @@ func (pool *TxPool) setNewHead(head *types.Header) { txc, _ := pool.reorgOnNewHead(ctx, head) m, r := txc.getLists() pool.relay.NewHead(pool.head, m, r) - pool.homestead = pool.config.IsHomestead(head.Number) - pool.signer = types.MakeSigner(pool.config, head.Number) + + // Update fork indicator by next pending block number + next := new(big.Int).Add(head.Number, big.NewInt(1)) + pool.istanbul = pool.config.IsIstanbul(next) } // Stop stops the light transaction pool @@ -378,7 +381,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead) + gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, true, pool.istanbul) if err != nil { return err } diff --git a/vendor/github.com/ethereum/go-ethereum/log/README.md b/vendor/github.com/ethereum/go-ethereum/log/README.md index b4476577b6..47426806dd 100644 --- a/vendor/github.com/ethereum/go-ethereum/log/README.md +++ b/vendor/github.com/ethereum/go-ethereum/log/README.md @@ -1,8 +1,8 @@ -![obligatory xkcd](http://imgs.xkcd.com/comics/standards.png) +![obligatory xkcd](https://imgs.xkcd.com/comics/standards.png) # log15 [![godoc reference](https://godoc.org/github.com/inconshreveable/log15?status.png)](https://godoc.org/github.com/inconshreveable/log15) [![Build Status](https://travis-ci.org/inconshreveable/log15.svg?branch=master)](https://travis-ci.org/inconshreveable/log15) -Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](http://golang.org/pkg/io/) and [`net/http`](http://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](http://golang.org/pkg/log/) package. +Package log15 provides an opinionated, simple toolkit for best-practice logging in Go (golang) that is both human and machine readable. It is modeled after the Go standard library's [`io`](https://golang.org/pkg/io/) and [`net/http`](https://golang.org/pkg/net/http/) packages and is an alternative to the standard library's [`log`](https://golang.org/pkg/log/) package. ## Features - A simple, easy-to-understand API diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/README.md b/vendor/github.com/ethereum/go-ethereum/metrics/README.md index bc2a45a838..e2d7945008 100644 --- a/vendor/github.com/ethereum/go-ethereum/metrics/README.md +++ b/vendor/github.com/ethereum/go-ethereum/metrics/README.md @@ -5,7 +5,7 @@ go-metrics Go port of Coda Hale's Metrics library: . -Documentation: . +Documentation: . Usage ----- @@ -128,7 +128,7 @@ go stathat.Stathat(metrics.DefaultRegistry, 10e9, "example@example.com") Maintain all metrics along with expvars at `/debug/metrics`: -This uses the same mechanism as [the official expvar](http://golang.org/pkg/expvar/) +This uses the same mechanism as [the official expvar](https://golang.org/pkg/expvar/) but exposed under `/debug/metrics`, which shows a json representation of all your usual expvars as well as all your go-metrics. diff --git a/vendor/github.com/ethereum/go-ethereum/metrics/gauge.go b/vendor/github.com/ethereum/go-ethereum/metrics/gauge.go index 0fbfdb8603..b6b2758b0d 100644 --- a/vendor/github.com/ethereum/go-ethereum/metrics/gauge.go +++ b/vendor/github.com/ethereum/go-ethereum/metrics/gauge.go @@ -6,6 +6,8 @@ import "sync/atomic" type Gauge interface { Snapshot() Gauge Update(int64) + Dec(int64) + Inc(int64) Value() int64 } @@ -65,6 +67,16 @@ func (GaugeSnapshot) Update(int64) { panic("Update called on a GaugeSnapshot") } +// Dec panics. +func (GaugeSnapshot) Dec(int64) { + panic("Dec called on a GaugeSnapshot") +} + +// Inc panics. +func (GaugeSnapshot) Inc(int64) { + panic("Inc called on a GaugeSnapshot") +} + // Value returns the value at the time the snapshot was taken. func (g GaugeSnapshot) Value() int64 { return int64(g) } @@ -77,6 +89,12 @@ func (NilGauge) Snapshot() Gauge { return NilGauge{} } // Update is a no-op. func (NilGauge) Update(v int64) {} +// Dec is a no-op. +func (NilGauge) Dec(i int64) {} + +// Inc is a no-op. +func (NilGauge) Inc(i int64) {} + // Value is a no-op. func (NilGauge) Value() int64 { return 0 } @@ -101,6 +119,16 @@ func (g *StandardGauge) Value() int64 { return atomic.LoadInt64(&g.value) } +// Dec decrements the gauge's current value by the given amount. +func (g *StandardGauge) Dec(i int64) { + atomic.AddInt64(&g.value, -i) +} + +// Inc increments the gauge's current value by the given amount. +func (g *StandardGauge) Inc(i int64) { + atomic.AddInt64(&g.value, i) +} + // FunctionalGauge returns value from given function type FunctionalGauge struct { value func() int64 @@ -118,3 +146,13 @@ func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) } func (FunctionalGauge) Update(int64) { panic("Update called on a FunctionalGauge") } + +// Dec panics. +func (FunctionalGauge) Dec(int64) { + panic("Dec called on a FunctionalGauge") +} + +// Inc panics. +func (FunctionalGauge) Inc(int64) { + panic("Inc called on a FunctionalGauge") +} diff --git a/vendor/github.com/ethereum/go-ethereum/miner/worker.go b/vendor/github.com/ethereum/go-ethereum/miner/worker.go index 4a9528c395..183499ec30 100644 --- a/vendor/github.com/ethereum/go-ethereum/miner/worker.go +++ b/vendor/github.com/ethereum/go-ethereum/miner/worker.go @@ -704,7 +704,7 @@ func (w *worker) updateSnapshot() { func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { snap := w.current.state.Snapshot() - receipt, _, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, *w.chain.GetVMConfig()) if err != nil { w.current.state.RevertToSnapshot(snap) return nil, err diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/dial.go b/vendor/github.com/ethereum/go-ethereum/p2p/dial.go index 8dee5063f1..68e06cce58 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/dial.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/dial.go @@ -33,12 +33,7 @@ const ( // private networks. dialHistoryExpiration = inboundThrottleTime + 5*time.Second - // Discovery lookups are throttled and can only run - // once every few seconds. - lookupInterval = 4 * time.Second - - // If no peers are found for this amount of time, the initial bootnodes are - // attempted to be connected. + // If no peers are found for this amount of time, the initial bootnodes are dialed. fallbackInterval = 20 * time.Second // Endpoint resolution is throttled with bounded backoff. @@ -52,6 +47,10 @@ type NodeDialer interface { Dial(*enode.Node) (net.Conn, error) } +type nodeResolver interface { + Resolve(*enode.Node) *enode.Node +} + // TCPDialer implements the NodeDialer interface by using a net.Dialer to // create TCP connections to nodes in the network type TCPDialer struct { @@ -69,7 +68,6 @@ func (t TCPDialer) Dial(dest *enode.Node) (net.Conn, error) { // of the main loop in Server.run. type dialstate struct { maxDynDials int - ntab discoverTable netrestrict *netutil.Netlist self enode.ID bootnodes []*enode.Node // default dials when there are no peers @@ -79,55 +77,23 @@ type dialstate struct { lookupRunning bool dialing map[enode.ID]connFlag lookupBuf []*enode.Node // current discovery lookup results - randomNodes []*enode.Node // filled from Table static map[enode.ID]*dialTask hist expHeap } -type discoverTable interface { - Close() - Resolve(*enode.Node) *enode.Node - LookupRandom() []*enode.Node - ReadRandomNodes([]*enode.Node) int -} - type task interface { Do(*Server) } -// A dialTask is generated for each node that is dialed. Its -// fields cannot be accessed while the task is running. -type dialTask struct { - flags connFlag - dest *enode.Node - lastResolved time.Time - resolveDelay time.Duration -} - -// discoverTask runs discovery table operations. -// Only one discoverTask is active at any time. -// discoverTask.Do performs a random lookup. -type discoverTask struct { - results []*enode.Node -} - -// A waitExpireTask is generated if there are no other tasks -// to keep the loop in Server.run ticking. -type waitExpireTask struct { - time.Duration -} - -func newDialState(self enode.ID, ntab discoverTable, maxdyn int, cfg *Config) *dialstate { +func newDialState(self enode.ID, maxdyn int, cfg *Config) *dialstate { s := &dialstate{ maxDynDials: maxdyn, - ntab: ntab, self: self, netrestrict: cfg.NetRestrict, log: cfg.Logger, static: make(map[enode.ID]*dialTask), dialing: make(map[enode.ID]connFlag), bootnodes: make([]*enode.Node, len(cfg.BootstrapNodes)), - randomNodes: make([]*enode.Node, maxdyn/2), } copy(s.bootnodes, cfg.BootstrapNodes) if s.log == nil { @@ -151,10 +117,6 @@ func (s *dialstate) removeStatic(n *enode.Node) { } func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Time) []task { - if s.start.IsZero() { - s.start = now - } - var newtasks []task addDial := func(flag connFlag, n *enode.Node) bool { if err := s.checkDial(n, peers); err != nil { @@ -166,20 +128,9 @@ func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Ti return true } - // Compute number of dynamic dials necessary at this point. - needDynDials := s.maxDynDials - for _, p := range peers { - if p.rw.is(dynDialedConn) { - needDynDials-- - } - } - for _, flag := range s.dialing { - if flag&dynDialedConn != 0 { - needDynDials-- - } + if s.start.IsZero() { + s.start = now } - - // Expire the dial history on every invocation. s.hist.expire(now) // Create dials for static nodes if they are not connected. @@ -194,6 +145,20 @@ func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Ti newtasks = append(newtasks, t) } } + + // Compute number of dynamic dials needed. + needDynDials := s.maxDynDials + for _, p := range peers { + if p.rw.is(dynDialedConn) { + needDynDials-- + } + } + for _, flag := range s.dialing { + if flag&dynDialedConn != 0 { + needDynDials-- + } + } + // If we don't have any peers whatsoever, try to dial a random bootnode. This // scenario is useful for the testnet (and private networks) where the discovery // table might be full of mostly bad peers, making it hard to find good ones. @@ -201,24 +166,12 @@ func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Ti bootnode := s.bootnodes[0] s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...) s.bootnodes = append(s.bootnodes, bootnode) - if addDial(dynDialedConn, bootnode) { needDynDials-- } } - // Use random nodes from the table for half of the necessary - // dynamic dials. - randomCandidates := needDynDials / 2 - if randomCandidates > 0 { - n := s.ntab.ReadRandomNodes(s.randomNodes) - for i := 0; i < randomCandidates && i < n; i++ { - if addDial(dynDialedConn, s.randomNodes[i]) { - needDynDials-- - } - } - } - // Create dynamic dials from random lookup results, removing tried - // items from the result buffer. + + // Create dynamic dials from discovery results. i := 0 for ; i < len(s.lookupBuf) && needDynDials > 0; i++ { if addDial(dynDialedConn, s.lookupBuf[i]) { @@ -226,10 +179,11 @@ func (s *dialstate) newTasks(nRunning int, peers map[enode.ID]*Peer, now time.Ti } } s.lookupBuf = s.lookupBuf[:copy(s.lookupBuf, s.lookupBuf[i:])] + // Launch a discovery lookup if more candidates are needed. if len(s.lookupBuf) < needDynDials && !s.lookupRunning { s.lookupRunning = true - newtasks = append(newtasks, &discoverTask{}) + newtasks = append(newtasks, &discoverTask{want: needDynDials - len(s.lookupBuf)}) } // Launch a timer to wait for the next node to expire if all @@ -279,6 +233,15 @@ func (s *dialstate) taskDone(t task, now time.Time) { } } +// A dialTask is generated for each node that is dialed. Its +// fields cannot be accessed while the task is running. +type dialTask struct { + flags connFlag + dest *enode.Node + lastResolved time.Time + resolveDelay time.Duration +} + func (t *dialTask) Do(srv *Server) { if t.dest.Incomplete() { if !t.resolve(srv) { @@ -304,8 +267,8 @@ func (t *dialTask) Do(srv *Server) { // discovery network with useless queries for nodes that don't exist. // The backoff delay resets when the node is found. func (t *dialTask) resolve(srv *Server) bool { - if srv.ntab == nil { - srv.log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled") + if srv.staticNodeResolver == nil { + srv.log.Debug("Can't resolve node", "id", t.dest.ID(), "err", "discovery is disabled") return false } if t.resolveDelay == 0 { @@ -314,20 +277,20 @@ func (t *dialTask) resolve(srv *Server) bool { if time.Since(t.lastResolved) < t.resolveDelay { return false } - resolved := srv.ntab.Resolve(t.dest) + resolved := srv.staticNodeResolver.Resolve(t.dest) t.lastResolved = time.Now() if resolved == nil { t.resolveDelay *= 2 if t.resolveDelay > maxResolveDelay { t.resolveDelay = maxResolveDelay } - srv.log.Debug("Resolving node failed", "id", t.dest.ID, "newdelay", t.resolveDelay) + srv.log.Debug("Resolving node failed", "id", t.dest.ID(), "newdelay", t.resolveDelay) return false } // The node was found. t.resolveDelay = initialResolveDelay t.dest = resolved - srv.log.Debug("Resolved node", "id", t.dest.ID, "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()}) + srv.log.Debug("Resolved node", "id", t.dest.ID(), "addr", &net.TCPAddr{IP: t.dest.IP(), Port: t.dest.TCP()}) return true } @@ -350,26 +313,34 @@ func (t *dialTask) String() string { return fmt.Sprintf("%v %x %v:%d", t.flags, id[:8], t.dest.IP(), t.dest.TCP()) } +// discoverTask runs discovery table operations. +// Only one discoverTask is active at any time. +// discoverTask.Do performs a random lookup. +type discoverTask struct { + want int + results []*enode.Node +} + func (t *discoverTask) Do(srv *Server) { - // newTasks generates a lookup task whenever dynamic dials are - // necessary. Lookups need to take some time, otherwise the - // event loop spins too fast. - next := srv.lastLookup.Add(lookupInterval) - if now := time.Now(); now.Before(next) { - time.Sleep(next.Sub(now)) - } - srv.lastLookup = time.Now() - t.results = srv.ntab.LookupRandom() + t.results = enode.ReadNodes(srv.discmix, t.want) } func (t *discoverTask) String() string { - s := "discovery lookup" + s := "discovery query" if len(t.results) > 0 { s += fmt.Sprintf(" (%d results)", len(t.results)) + } else { + s += fmt.Sprintf(" (want %d)", t.want) } return s } +// A waitExpireTask is generated if there are no other tasks +// to keep the loop in Server.run ticking. +type waitExpireTask struct { + time.Duration +} + func (t waitExpireTask) Do(*Server) { time.Sleep(t.Duration) } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/common.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/common.go index 3c080359fd..cef6a9fc4f 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/common.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/common.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/netutil" ) +// UDPConn is a network connection on which discovery can operate. type UDPConn interface { ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) @@ -32,7 +33,7 @@ type UDPConn interface { LocalAddr() net.Addr } -// Config holds Table-related settings. +// Config holds settings for the discovery listener. type Config struct { // These settings are required and configure the UDP listener: PrivateKey *ecdsa.PrivateKey @@ -50,7 +51,7 @@ func ListenUDP(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) { } // ReadPacket is a packet that couldn't be handled. Those packets are sent to the unhandled -// channel if configured. +// channel if configured. This is exported for internal use, do not use this type. type ReadPacket struct { Data []byte Addr *net.UDPAddr diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/lookup.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/lookup.go new file mode 100644 index 0000000000..f988e06838 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/lookup.go @@ -0,0 +1,209 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package discover + +import ( + "context" + + "github.com/ethereum/go-ethereum/p2p/enode" +) + +// lookup performs a network search for nodes close to the given target. It approaches the +// target by querying nodes that are closer to it on each iteration. The given target does +// not need to be an actual node identifier. +type lookup struct { + tab *Table + queryfunc func(*node) ([]*node, error) + replyCh chan []*node + cancelCh <-chan struct{} + asked, seen map[enode.ID]bool + result nodesByDistance + replyBuffer []*node + queries int +} + +type queryFunc func(*node) ([]*node, error) + +func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup { + it := &lookup{ + tab: tab, + queryfunc: q, + asked: make(map[enode.ID]bool), + seen: make(map[enode.ID]bool), + result: nodesByDistance{target: target}, + replyCh: make(chan []*node, alpha), + cancelCh: ctx.Done(), + queries: -1, + } + // Don't query further if we hit ourself. + // Unlikely to happen often in practice. + it.asked[tab.self().ID()] = true + return it +} + +// run runs the lookup to completion and returns the closest nodes found. +func (it *lookup) run() []*enode.Node { + for it.advance() { + } + return unwrapNodes(it.result.entries) +} + +// advance advances the lookup until any new nodes have been found. +// It returns false when the lookup has ended. +func (it *lookup) advance() bool { + for it.startQueries() { + select { + case nodes := <-it.replyCh: + it.replyBuffer = it.replyBuffer[:0] + for _, n := range nodes { + if n != nil && !it.seen[n.ID()] { + it.seen[n.ID()] = true + it.result.push(n, bucketSize) + it.replyBuffer = append(it.replyBuffer, n) + } + } + it.queries-- + if len(it.replyBuffer) > 0 { + return true + } + case <-it.cancelCh: + it.shutdown() + } + } + return false +} + +func (it *lookup) shutdown() { + for it.queries > 0 { + <-it.replyCh + it.queries-- + } + it.queryfunc = nil + it.replyBuffer = nil +} + +func (it *lookup) startQueries() bool { + if it.queryfunc == nil { + return false + } + + // The first query returns nodes from the local table. + if it.queries == -1 { + it.tab.mutex.Lock() + closest := it.tab.closest(it.result.target, bucketSize, false) + it.tab.mutex.Unlock() + it.queries = 1 + it.replyCh <- closest.entries + return true + } + + // Ask the closest nodes that we haven't asked yet. + for i := 0; i < len(it.result.entries) && it.queries < alpha; i++ { + n := it.result.entries[i] + if !it.asked[n.ID()] { + it.asked[n.ID()] = true + it.queries++ + go it.query(n, it.replyCh) + } + } + // The lookup ends when no more nodes can be asked. + return it.queries > 0 +} + +func (it *lookup) query(n *node, reply chan<- []*node) { + fails := it.tab.db.FindFails(n.ID(), n.IP()) + r, err := it.queryfunc(n) + if err == errClosed { + // Avoid recording failures on shutdown. + reply <- nil + return + } else if len(r) == 0 { + fails++ + it.tab.db.UpdateFindFails(n.ID(), n.IP(), fails) + it.tab.log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "err", err) + if fails >= maxFindnodeFailures { + it.tab.log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails) + it.tab.delete(n) + } + } else if fails > 0 { + // Reset failure counter because it counts _consecutive_ failures. + it.tab.db.UpdateFindFails(n.ID(), n.IP(), 0) + } + + // Grab as many nodes as possible. Some of them might not be alive anymore, but we'll + // just remove those again during revalidation. + for _, n := range r { + it.tab.addSeenNode(n) + } + reply <- r +} + +// lookupIterator performs lookup operations and iterates over all seen nodes. +// When a lookup finishes, a new one is created through nextLookup. +type lookupIterator struct { + buffer []*node + nextLookup lookupFunc + ctx context.Context + cancel func() + lookup *lookup +} + +type lookupFunc func(ctx context.Context) *lookup + +func newLookupIterator(ctx context.Context, next lookupFunc) *lookupIterator { + ctx, cancel := context.WithCancel(ctx) + return &lookupIterator{ctx: ctx, cancel: cancel, nextLookup: next} +} + +// Node returns the current node. +func (it *lookupIterator) Node() *enode.Node { + if len(it.buffer) == 0 { + return nil + } + return unwrapNode(it.buffer[0]) +} + +// Next moves to the next node. +func (it *lookupIterator) Next() bool { + // Consume next node in buffer. + if len(it.buffer) > 0 { + it.buffer = it.buffer[1:] + } + // Advance the lookup to refill the buffer. + for len(it.buffer) == 0 { + if it.ctx.Err() != nil { + it.lookup = nil + it.buffer = nil + return false + } + if it.lookup == nil { + it.lookup = it.nextLookup(it.ctx) + continue + } + if !it.lookup.advance() { + it.lookup = nil + continue + } + it.buffer = it.lookup.replyBuffer + } + return true +} + +// Close ends the iterator. +func (it *lookupIterator) Close() { + it.cancel() +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/discover/v4_udp.go b/vendor/github.com/ethereum/go-ethereum/p2p/discover/v4_udp.go index a8f7101b05..bfb66fcb19 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/discover/v4_udp.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/discover/v4_udp.go @@ -19,6 +19,7 @@ package discover import ( "bytes" "container/list" + "context" "crypto/ecdsa" crand "crypto/rand" "errors" @@ -207,7 +208,8 @@ type UDPv4 struct { addReplyMatcher chan *replyMatcher gotreply chan reply - closing chan struct{} + closeCtx context.Context + cancelCloseCtx func() } // replyMatcher represents a pending reply. @@ -256,20 +258,23 @@ type reply struct { } func ListenV4(c UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv4, error) { + closeCtx, cancel := context.WithCancel(context.Background()) t := &UDPv4{ conn: c, priv: cfg.PrivateKey, netrestrict: cfg.NetRestrict, localNode: ln, db: ln.Database(), - closing: make(chan struct{}), gotreply: make(chan reply), addReplyMatcher: make(chan *replyMatcher), + closeCtx: closeCtx, + cancelCloseCtx: cancel, log: cfg.Log, } if t.log == nil { t.log = log.Root() } + tab, err := newTable(t, ln.Database(), cfg.Bootnodes, t.log) if err != nil { return nil, err @@ -291,126 +296,13 @@ func (t *UDPv4) Self() *enode.Node { // Close shuts down the socket and aborts any running queries. func (t *UDPv4) Close() { t.closeOnce.Do(func() { - close(t.closing) + t.cancelCloseCtx() t.conn.Close() t.wg.Wait() t.tab.close() }) } -// ReadRandomNodes reads random nodes from the local table. -func (t *UDPv4) ReadRandomNodes(buf []*enode.Node) int { - return t.tab.ReadRandomNodes(buf) -} - -// LookupRandom finds random nodes in the network. -func (t *UDPv4) LookupRandom() []*enode.Node { - if t.tab.len() == 0 { - // All nodes were dropped, refresh. The very first query will hit this - // case and run the bootstrapping logic. - <-t.tab.refresh() - } - return t.lookupRandom() -} - -func (t *UDPv4) LookupPubkey(key *ecdsa.PublicKey) []*enode.Node { - if t.tab.len() == 0 { - // All nodes were dropped, refresh. The very first query will hit this - // case and run the bootstrapping logic. - <-t.tab.refresh() - } - return unwrapNodes(t.lookup(encodePubkey(key))) -} - -func (t *UDPv4) lookupRandom() []*enode.Node { - var target encPubkey - crand.Read(target[:]) - return unwrapNodes(t.lookup(target)) -} - -func (t *UDPv4) lookupSelf() []*enode.Node { - return unwrapNodes(t.lookup(encodePubkey(&t.priv.PublicKey))) -} - -// lookup performs a network search for nodes close to the given target. It approaches the -// target by querying nodes that are closer to it on each iteration. The given target does -// not need to be an actual node identifier. -func (t *UDPv4) lookup(targetKey encPubkey) []*node { - var ( - target = enode.ID(crypto.Keccak256Hash(targetKey[:])) - asked = make(map[enode.ID]bool) - seen = make(map[enode.ID]bool) - reply = make(chan []*node, alpha) - pendingQueries = 0 - result *nodesByDistance - ) - // Don't query further if we hit ourself. - // Unlikely to happen often in practice. - asked[t.Self().ID()] = true - - // Generate the initial result set. - t.tab.mutex.Lock() - result = t.tab.closest(target, bucketSize, false) - t.tab.mutex.Unlock() - - for { - // ask the alpha closest nodes that we haven't asked yet - for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { - n := result.entries[i] - if !asked[n.ID()] { - asked[n.ID()] = true - pendingQueries++ - go t.lookupWorker(n, targetKey, reply) - } - } - if pendingQueries == 0 { - // we have asked all closest nodes, stop the search - break - } - select { - case nodes := <-reply: - for _, n := range nodes { - if n != nil && !seen[n.ID()] { - seen[n.ID()] = true - result.push(n, bucketSize) - } - } - case <-t.tab.closeReq: - return nil // shutdown, no need to continue. - } - pendingQueries-- - } - return result.entries -} - -func (t *UDPv4) lookupWorker(n *node, targetKey encPubkey, reply chan<- []*node) { - fails := t.db.FindFails(n.ID(), n.IP()) - r, err := t.findnode(n.ID(), n.addr(), targetKey) - if err == errClosed { - // Avoid recording failures on shutdown. - reply <- nil - return - } else if len(r) == 0 { - fails++ - t.db.UpdateFindFails(n.ID(), n.IP(), fails) - t.log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "err", err) - if fails >= maxFindnodeFailures { - t.log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails) - t.tab.delete(n) - } - } else if fails > 0 { - // Reset failure counter because it counts _consecutive_ failures. - t.db.UpdateFindFails(n.ID(), n.IP(), 0) - } - - // Grab as many nodes as possible. Some of them might not be alive anymore, but we'll - // just remove those again during revalidation. - for _, n := range r { - t.tab.addSeenNode(n) - } - reply <- r -} - // Resolve searches for a specific node with the given ID and tries to get the most recent // version of the node record for it. It returns n if the node could not be resolved. func (t *UDPv4) Resolve(n *enode.Node) *enode.Node { @@ -498,6 +390,45 @@ func (t *UDPv4) makePing(toaddr *net.UDPAddr) *pingV4 { } } +// LookupPubkey finds the closest nodes to the given public key. +func (t *UDPv4) LookupPubkey(key *ecdsa.PublicKey) []*enode.Node { + if t.tab.len() == 0 { + // All nodes were dropped, refresh. The very first query will hit this + // case and run the bootstrapping logic. + <-t.tab.refresh() + } + return t.newLookup(t.closeCtx, encodePubkey(key)).run() +} + +// RandomNodes is an iterator yielding nodes from a random walk of the DHT. +func (t *UDPv4) RandomNodes() enode.Iterator { + return newLookupIterator(t.closeCtx, t.newRandomLookup) +} + +// lookupRandom implements transport. +func (t *UDPv4) lookupRandom() []*enode.Node { + return t.newRandomLookup(t.closeCtx).run() +} + +// lookupSelf implements transport. +func (t *UDPv4) lookupSelf() []*enode.Node { + return t.newLookup(t.closeCtx, encodePubkey(&t.priv.PublicKey)).run() +} + +func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup { + var target encPubkey + crand.Read(target[:]) + return t.newLookup(ctx, target) +} + +func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup { + target := enode.ID(crypto.Keccak256Hash(targetKey[:])) + it := newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) { + return t.findnode(n.ID(), n.addr(), targetKey) + }) + return it +} + // findnode sends a findnode request to the given node and waits until // the node has sent up to k neighbors. func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target encPubkey) ([]*node, error) { @@ -575,7 +506,7 @@ func (t *UDPv4) pending(id enode.ID, ip net.IP, ptype byte, callback replyMatchF select { case t.addReplyMatcher <- p: // loop will handle it - case <-t.closing: + case <-t.closeCtx.Done(): ch <- errClosed } return p @@ -589,7 +520,7 @@ func (t *UDPv4) handleReply(from enode.ID, fromIP net.IP, req packetV4) bool { case t.gotreply <- reply{from, fromIP, req, matched}: // loop will handle it return <-matched - case <-t.closing: + case <-t.closeCtx.Done(): return false } } @@ -635,7 +566,7 @@ func (t *UDPv4) loop() { resetTimeout() select { - case <-t.closing: + case <-t.closeCtx.Done(): for el := plist.Front(); el != nil; el = el.Next() { el.Value.(*replyMatcher).errc <- errClosed } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/enode/iter.go b/vendor/github.com/ethereum/go-ethereum/p2p/enode/iter.go new file mode 100644 index 0000000000..112b76d06a --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/p2p/enode/iter.go @@ -0,0 +1,286 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package enode + +import ( + "sync" + "time" +) + +// Iterator represents a sequence of nodes. The Next method moves to the next node in the +// sequence. It returns false when the sequence has ended or the iterator is closed. Close +// may be called concurrently with Next and Node, and interrupts Next if it is blocked. +type Iterator interface { + Next() bool // moves to next node + Node() *Node // returns current node + Close() // ends the iterator +} + +// ReadNodes reads at most n nodes from the given iterator. The return value contains no +// duplicates and no nil values. To prevent looping indefinitely for small repeating node +// sequences, this function calls Next at most n times. +func ReadNodes(it Iterator, n int) []*Node { + seen := make(map[ID]*Node, n) + for i := 0; i < n && it.Next(); i++ { + // Remove duplicates, keeping the node with higher seq. + node := it.Node() + prevNode, ok := seen[node.ID()] + if ok && prevNode.Seq() > node.Seq() { + continue + } + seen[node.ID()] = node + } + result := make([]*Node, 0, len(seen)) + for _, node := range seen { + result = append(result, node) + } + return result +} + +// IterNodes makes an iterator which runs through the given nodes once. +func IterNodes(nodes []*Node) Iterator { + return &sliceIter{nodes: nodes, index: -1} +} + +// CycleNodes makes an iterator which cycles through the given nodes indefinitely. +func CycleNodes(nodes []*Node) Iterator { + return &sliceIter{nodes: nodes, index: -1, cycle: true} +} + +type sliceIter struct { + mu sync.Mutex + nodes []*Node + index int + cycle bool +} + +func (it *sliceIter) Next() bool { + it.mu.Lock() + defer it.mu.Unlock() + + if len(it.nodes) == 0 { + return false + } + it.index++ + if it.index == len(it.nodes) { + if it.cycle { + it.index = 0 + } else { + it.nodes = nil + return false + } + } + return true +} + +func (it *sliceIter) Node() *Node { + if len(it.nodes) == 0 { + return nil + } + return it.nodes[it.index] +} + +func (it *sliceIter) Close() { + it.mu.Lock() + defer it.mu.Unlock() + + it.nodes = nil +} + +// Filter wraps an iterator such that Next only returns nodes for which +// the 'check' function returns true. +func Filter(it Iterator, check func(*Node) bool) Iterator { + return &filterIter{it, check} +} + +type filterIter struct { + Iterator + check func(*Node) bool +} + +func (f *filterIter) Next() bool { + for f.Iterator.Next() { + if f.check(f.Node()) { + return true + } + } + return false +} + +// FairMix aggregates multiple node iterators. The mixer itself is an iterator which ends +// only when Close is called. Source iterators added via AddSource are removed from the +// mix when they end. +// +// The distribution of nodes returned by Next is approximately fair, i.e. FairMix +// attempts to draw from all sources equally often. However, if a certain source is slow +// and doesn't return a node within the configured timeout, a node from any other source +// will be returned. +// +// It's safe to call AddSource and Close concurrently with Next. +type FairMix struct { + wg sync.WaitGroup + fromAny chan *Node + timeout time.Duration + cur *Node + + mu sync.Mutex + closed chan struct{} + sources []*mixSource + last int +} + +type mixSource struct { + it Iterator + next chan *Node + timeout time.Duration +} + +// NewFairMix creates a mixer. +// +// The timeout specifies how long the mixer will wait for the next fairly-chosen source +// before giving up and taking a node from any other source. A good way to set the timeout +// is deciding how long you'd want to wait for a node on average. Passing a negative +// timeout makes the mixer completely fair. +func NewFairMix(timeout time.Duration) *FairMix { + m := &FairMix{ + fromAny: make(chan *Node), + closed: make(chan struct{}), + timeout: timeout, + } + return m +} + +// AddSource adds a source of nodes. +func (m *FairMix) AddSource(it Iterator) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.closed == nil { + return + } + m.wg.Add(1) + source := &mixSource{it, make(chan *Node), m.timeout} + m.sources = append(m.sources, source) + go m.runSource(m.closed, source) +} + +// Close shuts down the mixer and all current sources. +// Calling this is required to release resources associated with the mixer. +func (m *FairMix) Close() { + m.mu.Lock() + defer m.mu.Unlock() + + if m.closed == nil { + return + } + for _, s := range m.sources { + s.it.Close() + } + close(m.closed) + m.wg.Wait() + close(m.fromAny) + m.sources = nil + m.closed = nil +} + +// Next returns a node from a random source. +func (m *FairMix) Next() bool { + m.cur = nil + + var timeout <-chan time.Time + if m.timeout >= 0 { + timer := time.NewTimer(m.timeout) + timeout = timer.C + defer timer.Stop() + } + for { + source := m.pickSource() + if source == nil { + return m.nextFromAny() + } + select { + case n, ok := <-source.next: + if ok { + m.cur = n + source.timeout = m.timeout + return true + } + // This source has ended. + m.deleteSource(source) + case <-timeout: + source.timeout /= 2 + return m.nextFromAny() + } + } +} + +// Node returns the current node. +func (m *FairMix) Node() *Node { + return m.cur +} + +// nextFromAny is used when there are no sources or when the 'fair' choice +// doesn't turn up a node quickly enough. +func (m *FairMix) nextFromAny() bool { + n, ok := <-m.fromAny + if ok { + m.cur = n + } + return ok +} + +// pickSource chooses the next source to read from, cycling through them in order. +func (m *FairMix) pickSource() *mixSource { + m.mu.Lock() + defer m.mu.Unlock() + + if len(m.sources) == 0 { + return nil + } + m.last = (m.last + 1) % len(m.sources) + return m.sources[m.last] +} + +// deleteSource deletes a source. +func (m *FairMix) deleteSource(s *mixSource) { + m.mu.Lock() + defer m.mu.Unlock() + + for i := range m.sources { + if m.sources[i] == s { + copy(m.sources[i:], m.sources[i+1:]) + m.sources[len(m.sources)-1] = nil + m.sources = m.sources[:len(m.sources)-1] + break + } + } +} + +// runSource reads a single source in a loop. +func (m *FairMix) runSource(closed chan struct{}, s *mixSource) { + defer m.wg.Done() + defer close(s.next) + for s.it.Next() { + n := s.it.Node() + select { + case s.next <- n: + case m.fromAny <- n: + case <-closed: + return + } + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/enode/urlv4.go b/vendor/github.com/ethereum/go-ethereum/p2p/enode/urlv4.go index 2372d4820b..a9a3d1374e 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/enode/urlv4.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/enode/urlv4.go @@ -125,15 +125,17 @@ func parseComplete(rawurl string) (*Node, error) { return nil, fmt.Errorf("invalid public key (%v)", err) } // Parse the IP address. - host, port, err := net.SplitHostPort(u.Host) + ips, err := net.LookupIP(u.Hostname()) if err != nil { - return nil, fmt.Errorf("invalid host: %v", err) + return nil, err } - if ip = net.ParseIP(host); ip == nil { - return nil, errors.New("invalid IP address") + ip = ips[0] + // Ensure the IP is 4 bytes long for IPv4 addresses. + if ipv4 := ip.To4(); ipv4 != nil { + ip = ipv4 } // Parse the port numbers. - if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil { + if tcpPort, err = strconv.ParseUint(u.Port(), 10, 16); err != nil { return nil, errors.New("invalid port") } udpPort = tcpPort diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/message.go b/vendor/github.com/ethereum/go-ethereum/p2p/message.go index b987732225..10b55a939c 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/message.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/message.go @@ -39,9 +39,13 @@ import ( // separate Msg with a bytes.Reader as Payload for each send. type Msg struct { Code uint64 - Size uint32 // size of the paylod + Size uint32 // Size of the raw payload Payload io.Reader ReceivedAt time.Time + + meterCap Cap // Protocol name and version for egress metering + meterCode uint64 // Message within protocol for egress metering + meterSize uint32 // Compressed message size for ingress metering } // Decode parses the RLP content of a message into diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go b/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go index c04e5ab4c3..8b29efdcdb 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/metrics.go @@ -45,7 +45,7 @@ var ( ingressTrafficMeter = metrics.NewRegisteredMeter(MetricsInboundTraffic, nil) // Meter metering the cumulative ingress traffic egressConnectMeter = metrics.NewRegisteredMeter(MetricsOutboundConnects, nil) // Meter counting the egress connections egressTrafficMeter = metrics.NewRegisteredMeter(MetricsOutboundTraffic, nil) // Meter metering the cumulative egress traffic - activePeerCounter = metrics.NewRegisteredCounter("p2p/peers", nil) // Gauge tracking the current peer count + activePeerGauge = metrics.NewRegisteredGauge("p2p/peers", nil) // Gauge tracking the current peer count PeerIngressRegistry = metrics.NewPrefixedChildRegistry(metrics.EphemeralRegistry, MetricsInboundTraffic+"/") // Registry containing the peer ingress PeerEgressRegistry = metrics.NewPrefixedChildRegistry(metrics.EphemeralRegistry, MetricsOutboundTraffic+"/") // Registry containing the peer egress @@ -124,7 +124,7 @@ func newMeteredConn(conn net.Conn, ingress bool, ip net.IP) net.Conn { } else { egressConnectMeter.Mark(1) } - activePeerCounter.Inc(1) + activePeerGauge.Inc(1) return &meteredConn{ Conn: conn, @@ -200,7 +200,7 @@ func (c *meteredConn) Close() error { IP: c.ip, Elapsed: time.Since(c.connected), }) - activePeerCounter.Dec(1) + activePeerGauge.Dec(1) return err } id := c.id @@ -212,7 +212,7 @@ func (c *meteredConn) Close() error { IP: c.ip, ID: id, }) - activePeerCounter.Dec(1) + activePeerGauge.Dec(1) return err } ingress, egress := uint64(c.ingressMeter.Count()), uint64(c.egressMeter.Count()) @@ -233,6 +233,6 @@ func (c *meteredConn) Close() error { Ingress: ingress, Egress: egress, }) - activePeerCounter.Dec(1) + activePeerGauge.Dec(1) return err } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/peer.go b/vendor/github.com/ethereum/go-ethereum/p2p/peer.go index 372ba8d027..9a9788bc17 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/peer.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/peer.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/rlp" @@ -300,6 +301,9 @@ func (p *Peer) handle(msg Msg) error { if err != nil { return fmt.Errorf("msg code out of range: %v", msg.Code) } + if metrics.Enabled { + metrics.GetOrRegisterMeter(fmt.Sprintf("%s/%s/%d/%#02x", MetricsInboundTraffic, proto.Name, proto.Version, msg.Code-proto.offset), nil).Mark(int64(msg.meterSize)) + } select { case proto.in <- msg: return nil @@ -398,7 +402,11 @@ func (rw *protoRW) WriteMsg(msg Msg) (err error) { if msg.Code >= rw.Length { return newPeerError(errInvalidMsgCode, "not handled") } + msg.meterCap = rw.cap() + msg.meterCode = msg.Code + msg.Code += rw.offset + select { case <-rw.wstart: err = rw.w.WriteMsg(msg) diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go b/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go index 9ce4c20203..fa23a087c2 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/protocol.go @@ -54,6 +54,11 @@ type Protocol struct { // but returns nil, it is assumed that the protocol handshake is still running. PeerInfo func(id enode.ID) interface{} + // DialCandidates, if non-nil, is a way to tell Server about protocol-specific nodes + // that should be dialed. The server continuously reads nodes from the iterator and + // attempts to create connections to them. + DialCandidates enode.Iterator + // Attributes contains protocol specific information for the node record. Attributes []enr.Entry } diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go b/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go index 0636431f53..115021fa94 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/rlpx.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "github.com/golang/snappy" "golang.org/x/crypto/sha3" @@ -46,10 +47,10 @@ import ( const ( maxUint24 = ^uint32(0) >> 8 - sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 - sigLen = 65 // elliptic S256 - pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte - shaLen = 32 // hash length (for nonce etc) + sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 + sigLen = crypto.SignatureLength // elliptic S256 + pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte + shaLen = 32 // hash length (for nonce etc) authMsgLen = sigLen + shaLen + pubLen + shaLen + 1 authRespLen = pubLen + shaLen + 1 @@ -602,6 +603,10 @@ func (rw *rlpxFrameRW) WriteMsg(msg Msg) error { msg.Payload = bytes.NewReader(payload) msg.Size = uint32(len(payload)) } + msg.meterSize = msg.Size + if metrics.Enabled && msg.meterCap.Name != "" { // don't meter non-subprotocol messages + metrics.GetOrRegisterMeter(fmt.Sprintf("%s/%s/%d/%#02x", MetricsOutboundTraffic, msg.meterCap.Name, msg.meterCap.Version, msg.meterCode), nil).Mark(int64(msg.meterSize)) + } // write header headbuf := make([]byte, 32) fsize := uint32(len(ptype)) + msg.Size @@ -686,6 +691,7 @@ func (rw *rlpxFrameRW) ReadMsg() (msg Msg, err error) { return msg, err } msg.Size = uint32(content.Len()) + msg.meterSize = msg.Size msg.Payload = content // if snappy is enabled, verify and decompress message diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/server.go b/vendor/github.com/ethereum/go-ethereum/p2p/server.go index 692c9eb7d9..246148741f 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/server.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/server.go @@ -45,6 +45,11 @@ import ( const ( defaultDialTimeout = 15 * time.Second + // This is the fairness knob for the discovery mixer. When looking for peers, we'll + // wait this long for a single source of candidates before moving on and trying other + // sources. + discmixTimeout = 5 * time.Second + // Connectivity defaults. maxActiveDialTasks = 16 defaultMaxPendingPeers = 50 @@ -167,16 +172,20 @@ type Server struct { lock sync.Mutex // protects running running bool - nodedb *enode.DB - localnode *enode.LocalNode - ntab discoverTable listener net.Listener ourHandshake *protoHandshake - DiscV5 *discv5.Network loopWG sync.WaitGroup // loop, listenLoop peerFeed event.Feed log log.Logger + nodedb *enode.DB + localnode *enode.LocalNode + ntab *discover.UDPv4 + DiscV5 *discv5.Network + discmix *enode.FairMix + + staticNodeResolver nodeResolver + // Channels into the run loop. quit chan struct{} addstatic chan *enode.Node @@ -470,7 +479,7 @@ func (srv *Server) Start() (err error) { } dynPeers := srv.maxDialedConns() - dialer := newDialState(srv.localnode.ID(), srv.ntab, dynPeers, &srv.Config) + dialer := newDialState(srv.localnode.ID(), dynPeers, &srv.Config) srv.loopWG.Add(1) go srv.run(dialer) return nil @@ -521,6 +530,18 @@ func (srv *Server) setupLocalNode() error { } func (srv *Server) setupDiscovery() error { + srv.discmix = enode.NewFairMix(discmixTimeout) + + // Add protocol-specific discovery sources. + added := make(map[string]bool) + for _, proto := range srv.Protocols { + if proto.DialCandidates != nil && !added[proto.Name] { + srv.discmix.AddSource(proto.DialCandidates) + added[proto.Name] = true + } + } + + // Don't listen on UDP endpoint if DHT is disabled. if srv.NoDiscovery && !srv.DiscoveryV5 { return nil } @@ -562,7 +583,10 @@ func (srv *Server) setupDiscovery() error { return err } srv.ntab = ntab + srv.discmix.AddSource(ntab.RandomNodes()) + srv.staticNodeResolver = ntab } + // Discovery V5 if srv.DiscoveryV5 { var ntab *discv5.Network @@ -620,6 +644,7 @@ func (srv *Server) run(dialstate dialer) { srv.log.Info("Started P2P networking", "self", srv.localnode.Node().URLv4()) defer srv.loopWG.Done() defer srv.nodedb.Close() + defer srv.discmix.Close() var ( peers = make(map[enode.ID]*Peer) diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/simulations/adapters/types.go b/vendor/github.com/ethereum/go-ethereum/p2p/simulations/adapters/types.go index f65ce7b605..850de96a15 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/simulations/adapters/types.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/simulations/adapters/types.go @@ -101,6 +101,11 @@ type NodeConfig struct { // services registered by calling the RegisterService function) Services []string + // Properties are the names of the properties this node should hold + // within running services (e.g. "bootnode", "lightnode" or any custom values) + // These values need to be checked and acted upon by node Services + Properties []string + // Enode node *enode.Node @@ -120,6 +125,7 @@ type nodeConfigJSON struct { PrivateKey string `json:"private_key"` Name string `json:"name"` Services []string `json:"services"` + Properties []string `json:"properties"` EnableMsgEvents bool `json:"enable_msg_events"` Port uint16 `json:"port"` } @@ -131,6 +137,7 @@ func (n *NodeConfig) MarshalJSON() ([]byte, error) { ID: n.ID.String(), Name: n.Name, Services: n.Services, + Properties: n.Properties, Port: n.Port, EnableMsgEvents: n.EnableMsgEvents, } @@ -168,6 +175,7 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error { n.Name = confJSON.Name n.Services = confJSON.Services + n.Properties = confJSON.Properties n.Port = confJSON.Port n.EnableMsgEvents = confJSON.EnableMsgEvents diff --git a/vendor/github.com/ethereum/go-ethereum/p2p/simulations/network.go b/vendor/github.com/ethereum/go-ethereum/p2p/simulations/network.go index f03c953e89..58fd9a28b0 100644 --- a/vendor/github.com/ethereum/go-ethereum/p2p/simulations/network.go +++ b/vendor/github.com/ethereum/go-ethereum/p2p/simulations/network.go @@ -56,6 +56,9 @@ type Network struct { Nodes []*Node `json:"nodes"` nodeMap map[enode.ID]int + // Maps a node property string to node indexes of all nodes that hold this property + propertyMap map[string][]int + Conns []*Conn `json:"conns"` connMap map[string]int @@ -71,6 +74,7 @@ func NewNetwork(nodeAdapter adapters.NodeAdapter, conf *NetworkConfig) *Network NetworkConfig: *conf, nodeAdapter: nodeAdapter, nodeMap: make(map[enode.ID]int), + propertyMap: make(map[string][]int), connMap: make(map[string]int), quitc: make(chan struct{}), } @@ -120,9 +124,16 @@ func (net *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) Config: conf, } log.Trace("Node created", "id", conf.ID) - net.nodeMap[conf.ID] = len(net.Nodes) + + nodeIndex := len(net.Nodes) + net.nodeMap[conf.ID] = nodeIndex net.Nodes = append(net.Nodes, node) + // Register any node properties with the network-level propertyMap + for _, property := range conf.Properties { + net.propertyMap[property] = append(net.propertyMap[property], nodeIndex) + } + // emit a "control" event net.events.Send(ControlEvent(node)) @@ -410,7 +421,7 @@ func (net *Network) getNode(id enode.ID) *Node { return net.Nodes[i] } -// GetNode gets the node with the given name, returning nil if the node does +// GetNodeByName gets the node with the given name, returning nil if the node does // not exist func (net *Network) GetNodeByName(name string) *Node { net.lock.RLock() @@ -427,19 +438,104 @@ func (net *Network) getNodeByName(name string) *Node { return nil } -// GetNodes returns the existing nodes -func (net *Network) GetNodes() (nodes []*Node) { +// GetNodeIDs returns the IDs of all existing nodes +// Nodes can optionally be excluded by specifying their enode.ID. +func (net *Network) GetNodeIDs(excludeIDs ...enode.ID) []enode.ID { + net.lock.RLock() + defer net.lock.RUnlock() + + return net.getNodeIDs(excludeIDs) +} + +func (net *Network) getNodeIDs(excludeIDs []enode.ID) []enode.ID { + // Get all curent nodeIDs + nodeIDs := make([]enode.ID, 0, len(net.nodeMap)) + for id := range net.nodeMap { + nodeIDs = append(nodeIDs, id) + } + + if len(excludeIDs) > 0 { + // Return the difference of nodeIDs and excludeIDs + return filterIDs(nodeIDs, excludeIDs) + } else { + return nodeIDs + } +} + +// GetNodes returns the existing nodes. +// Nodes can optionally be excluded by specifying their enode.ID. +func (net *Network) GetNodes(excludeIDs ...enode.ID) []*Node { + net.lock.RLock() + defer net.lock.RUnlock() + + return net.getNodes(excludeIDs) +} + +func (net *Network) getNodes(excludeIDs []enode.ID) []*Node { + if len(excludeIDs) > 0 { + nodeIDs := net.getNodeIDs(excludeIDs) + return net.getNodesByID(nodeIDs) + } else { + return net.Nodes + } +} + +// GetNodesByID returns existing nodes with the given enode.IDs. +// If a node doesn't exist with a given enode.ID, it is ignored. +func (net *Network) GetNodesByID(nodeIDs []enode.ID) []*Node { + net.lock.RLock() + defer net.lock.RUnlock() + + return net.getNodesByID(nodeIDs) +} + +func (net *Network) getNodesByID(nodeIDs []enode.ID) []*Node { + nodes := make([]*Node, 0, len(nodeIDs)) + for _, id := range nodeIDs { + node := net.getNode(id) + if node != nil { + nodes = append(nodes, node) + } + } + + return nodes +} + +// GetNodesByProperty returns existing nodes that have the given property string registered in their NodeConfig +func (net *Network) GetNodesByProperty(property string) []*Node { net.lock.RLock() defer net.lock.RUnlock() - return net.getNodes() + return net.getNodesByProperty(property) } -func (net *Network) getNodes() (nodes []*Node) { - nodes = append(nodes, net.Nodes...) +func (net *Network) getNodesByProperty(property string) []*Node { + nodes := make([]*Node, 0, len(net.propertyMap[property])) + for _, nodeIndex := range net.propertyMap[property] { + nodes = append(nodes, net.Nodes[nodeIndex]) + } + return nodes } +// GetNodeIDsByProperty returns existing node's enode IDs that have the given property string registered in the NodeConfig +func (net *Network) GetNodeIDsByProperty(property string) []enode.ID { + net.lock.RLock() + defer net.lock.RUnlock() + + return net.getNodeIDsByProperty(property) +} + +func (net *Network) getNodeIDsByProperty(property string) []enode.ID { + nodeIDs := make([]enode.ID, 0, len(net.propertyMap[property])) + for _, nodeIndex := range net.propertyMap[property] { + node := net.Nodes[nodeIndex] + nodeIDs = append(nodeIDs, node.ID()) + } + + return nodeIDs +} + // GetRandomUpNode returns a random node on the network, which is running. func (net *Network) GetRandomUpNode(excludeIDs ...enode.ID) *Node { net.lock.RLock() @@ -469,7 +565,7 @@ func (net *Network) GetRandomDownNode(excludeIDs ...enode.ID) *Node { } func (net *Network) getDownNodeIDs() (ids []enode.ID) { - for _, node := range net.getNodes() { + for _, node := range net.Nodes { if !node.Up() { ids = append(ids, node.ID()) } @@ -477,6 +573,13 @@ func (net *Network) getDownNodeIDs() (ids []enode.ID) { return ids } +// GetRandomNode returns a random node on the network, regardless of whether it is running or not +func (net *Network) GetRandomNode(excludeIDs ...enode.ID) *Node { + net.lock.RLock() + defer net.lock.RUnlock() + return net.getRandomNode(net.getNodeIDs(nil), excludeIDs) // no need to exclude twice +} + func (net *Network) getRandomNode(ids []enode.ID, excludeIDs []enode.ID) *Node { filtered := filterIDs(ids, excludeIDs) @@ -616,6 +719,7 @@ func (net *Network) Reset() { //re-initialize the maps net.connMap = make(map[string]int) net.nodeMap = make(map[enode.ID]int) + net.propertyMap = make(map[string][]int) net.Nodes = nil net.Conns = nil @@ -634,12 +738,14 @@ type Node struct { upMu sync.RWMutex } +// Up returns whether the node is currently up (online) func (n *Node) Up() bool { n.upMu.RLock() defer n.upMu.RUnlock() return n.up } +// SetUp sets the up (online) status of the nodes with the given value func (n *Node) SetUp(up bool) { n.upMu.Lock() defer n.upMu.Unlock() diff --git a/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go index 36f13d1787..967cba5bc4 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go +++ b/vendor/github.com/ethereum/go-ethereum/params/bootnodes.go @@ -29,13 +29,6 @@ var MainnetBootnodes = []string{ "enode://715171f50508aba88aecd1250af392a45a330af91d7b90701c436b618c86aaa1589c9184561907bebbb56439b8f8787bc01f49a7c77276c58c1b09822d75e8e8@52.231.165.108:30303", // bootnode-azure-koreasouth-001 "enode://5d6d7cd20d6da4bb83a1d28cadb5d409b64edf314c0335df658c1a54e32c7c4a7ab7823d57c39b6a757556e68ff1df17c748b698544a55cb488b52479a92b60f@104.42.217.25:30303", // bootnode-azure-westus-001 - // Ethereum Foundation Go Bootnodes (legacy) - "enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303", // IE - "enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", // US-WEST - "enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", // BR - "enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", // AU - "enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", // SG - // Ethereum Foundation C++ Bootnodes "enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303", // DE } diff --git a/vendor/github.com/ethereum/go-ethereum/params/config.go b/vendor/github.com/ethereum/go-ethereum/params/config.go index 200add01b5..c90de56dc3 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/config.go +++ b/vendor/github.com/ethereum/go-ethereum/params/config.go @@ -65,16 +65,16 @@ var ( ByzantiumBlock: big.NewInt(4370000), ConstantinopleBlock: big.NewInt(7280000), PetersburgBlock: big.NewInt(7280000), - IstanbulBlock: nil, + IstanbulBlock: big.NewInt(9069000), Ethash: new(EthashConfig), } // MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network. MainnetTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 253, - SectionHead: common.HexToHash("0xf35fabd036e2030196183bb70ae194f6ce1ea7b58559e3825c168f1df9c0a258"), - CHTRoot: common.HexToHash("0x8992849e2be3390696eaf66312626e484045501cd3ec207922c27a6a80a7bb07"), - BloomRoot: common.HexToHash("0xcc510b51ca4d73fb3fdf43208d73286f8f23817cdc31b8ea9f4de8d645f07df4"), + SectionIndex: 270, + SectionHead: common.HexToHash("0xb67c33d838a60c282c2fb49b188fbbac1ef8565ffb4a1c4909b0a05885e72e40"), + CHTRoot: common.HexToHash("0x781daa4607782300da85d440df3813ba38a1262585231e35e9480726de81dbfc"), + BloomRoot: common.HexToHash("0xfd8951fa6d779cbc981df40dc31056ed1a549db529349d7dfae016f9d96cae72"), } // MainnetCheckpointOracle contains a set of configs for the main network oracle. @@ -103,16 +103,16 @@ var ( ByzantiumBlock: big.NewInt(1700000), ConstantinopleBlock: big.NewInt(4230000), PetersburgBlock: big.NewInt(4939394), - IstanbulBlock: nil, + IstanbulBlock: big.NewInt(6485846), Ethash: new(EthashConfig), } // TestnetTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network. TestnetTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 187, - SectionHead: common.HexToHash("0x7d6db64d8ec43303e4392fb726d2346f7231b246decca3d8140dd7e2c0d0b07d"), - CHTRoot: common.HexToHash("0xa5095e1a004a8642fb93ca682eb91e8f20ef5bce151e47404fbb68772d17705b"), - BloomRoot: common.HexToHash("0x90b28050f948ec6fb35b23a91d9aed38ce0c92d3cdd6e1d383c1bddf8b4071cf"), + SectionIndex: 204, + SectionHead: common.HexToHash("0xa39168b51c3205456f30ce6a91f3590a43295b15a1c8c2ab86bb8c06b8ad1808"), + CHTRoot: common.HexToHash("0x9a3654147b79882bfc4e16fbd3421512aa7e4dfadc6c511923980e0877bdf3b4"), + BloomRoot: common.HexToHash("0xe72b979522d94fa45c1331639316da234a9bb85062d64d72e13afe1d3f5c17d5"), } // TestnetCheckpointOracle contains a set of configs for the Ropsten test network oracle. @@ -141,7 +141,7 @@ var ( ByzantiumBlock: big.NewInt(1035301), ConstantinopleBlock: big.NewInt(3660663), PetersburgBlock: big.NewInt(4321234), - IstanbulBlock: nil, + IstanbulBlock: big.NewInt(5435345), Clique: &CliqueConfig{ Period: 15, Epoch: 30000, @@ -150,10 +150,10 @@ var ( // RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network. RinkebyTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 148, - SectionHead: common.HexToHash("0x45918f4686732c2a3e80827e1bc39cdb6a27fa362ddfe1fdfb61c69a7f1df1a9"), - CHTRoot: common.HexToHash("0x8ac7046391fec14834a2a0183513937c0b5f696666545991477d24b067008961"), - BloomRoot: common.HexToHash("0xfe4b852517612d7da54bf7e9fc18861a83171a93c72583bb6a61893b74422168"), + SectionIndex: 163, + SectionHead: common.HexToHash("0x36e5deaa46f258bece94b05d8e10f1ef68f422fb62ed47a2b6e616aa26e84997"), + CHTRoot: common.HexToHash("0x829b9feca1c2cdf5a4cf3efac554889e438ee4df8718c2ce3e02555a02d9e9e5"), + BloomRoot: common.HexToHash("0x58c01de24fdae7c082ebbe7665f189d0aa4d90ee10e72086bf56651c63269e54"), } // RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle. @@ -180,7 +180,7 @@ var ( ByzantiumBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), - IstanbulBlock: nil, + IstanbulBlock: big.NewInt(1561651), Clique: &CliqueConfig{ Period: 15, Epoch: 30000, @@ -189,10 +189,10 @@ var ( // GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network. GoerliTrustedCheckpoint = &TrustedCheckpoint{ - SectionIndex: 32, - SectionHead: common.HexToHash("0x50eaedd8361fa9edd0ac2dec410310b9bdf67b963b60f3b1dce47f84b30670f9"), - CHTRoot: common.HexToHash("0x6504db73139f75ffa9102ae980e41b361cf3d5b66cea06c79cde9f457368820c"), - BloomRoot: common.HexToHash("0x7551ae027bb776252a20ded51ee2ff0cbfbd1d8d57261b9161cc1f2f80237001"), + SectionIndex: 47, + SectionHead: common.HexToHash("0x00c5b54c6c9a73660501fd9273ccdb4c5bbdbe5d7b8b650e28f881ec9d2337f6"), + CHTRoot: common.HexToHash("0xef35caa155fd659f57167e7d507de2f8132cbb31f771526481211d8a977d704c"), + BloomRoot: common.HexToHash("0xbda330402f66008d52e7adc748da28535b1212a7912a21244acd2ba77ff0ff06"), } // GoerliCheckpointOracle contains a set of configs for the Goerli test network oracle. @@ -213,16 +213,16 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil} TestRules = TestChainConfig.Rules(new(big.Int)) ) @@ -415,6 +415,42 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi return lasterr } +// CheckConfigForkOrder checks that we don't "skip" any forks, geth isn't pluggable enough +// to guarantee that forks +func (c *ChainConfig) CheckConfigForkOrder() error { + type fork struct { + name string + block *big.Int + } + var lastFork fork + for _, cur := range []fork{ + {"homesteadBlock", c.HomesteadBlock}, + {"eip150Block", c.EIP150Block}, + {"eip155Block", c.EIP155Block}, + {"eip158Block", c.EIP158Block}, + {"byzantiumBlock", c.ByzantiumBlock}, + {"constantinopleBlock", c.ConstantinopleBlock}, + {"petersburgBlock", c.PetersburgBlock}, + {"istanbulBlock", c.IstanbulBlock}, + } { + if lastFork.name != "" { + // Next one must be higher number + if lastFork.block == nil && cur.block != nil { + return fmt.Errorf("unsupported fork ordering: %v not enabled, but %v enabled at %v", + lastFork.name, cur.name, cur.block) + } + if lastFork.block != nil && cur.block != nil { + if lastFork.block.Cmp(cur.block) > 0 { + return fmt.Errorf("unsupported fork ordering: %v enabled at %v, but %v enabled at %v", + lastFork.name, lastFork.block, cur.name, cur.block) + } + } + } + lastFork = cur + } + return nil +} + func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError { if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) { return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock) diff --git a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go index 943788be8c..11b858a61c 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go +++ b/vendor/github.com/ethereum/go-ethereum/params/protocol_params.go @@ -52,22 +52,32 @@ const ( NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value + SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed + SstoreNoopGasEIP2200 uint64 = 800 // Once per SSTORE operation if the value doesn't change. + SstoreDirtyGasEIP2200 uint64 = 800 // Once per SSTORE operation if a dirty value is changed. + SstoreInitGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero + SstoreInitRefundEIP2200 uint64 = 19200 // Once per SSTORE operation for resetting to the original zero value + SstoreCleanGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else + SstoreCleanRefundEIP2200 uint64 = 4200 // Once per SSTORE operation for resetting to the original non-zero value + SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot + JumpdestGas uint64 = 1 // Once per JUMPDEST operation. EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. - CreateDataGas uint64 = 200 // - CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. - ExpGas uint64 = 10 // Once per EXP instruction - LogGas uint64 = 375 // Per LOG* operation. - CopyGas uint64 = 3 // - StackLimit uint64 = 1024 // Maximum size of VM stack allowed. - TierStepGas uint64 = 0 // Once per operation, for a selection of them. - LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. - CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. - Create2Gas uint64 = 32000 // Once per CREATE2 operation - SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. - MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. - TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + CreateDataGas uint64 = 200 // + CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. + ExpGas uint64 = 10 // Once per EXP instruction + LogGas uint64 = 375 // Per LOG* operation. + CopyGas uint64 = 3 // + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. + TierStepGas uint64 = 0 // Once per operation, for a selection of them. + LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. + CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. + Create2Gas uint64 = 32000 // Once per CREATE2 operation + SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. + MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. + TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) // These have been changed during the course of the chain CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction. diff --git a/vendor/github.com/ethereum/go-ethereum/params/version.go b/vendor/github.com/ethereum/go-ethereum/params/version.go index 87934df80d..efb92abb28 100644 --- a/vendor/github.com/ethereum/go-ethereum/params/version.go +++ b/vendor/github.com/ethereum/go-ethereum/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 9 // Minor version component of the current release - VersionPatch = 2 // Patch version component of the current release + VersionPatch = 7 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string ) diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/decode.go b/vendor/github.com/ethereum/go-ethereum/rlp/decode.go index 4f29f2fb03..524395915d 100644 --- a/vendor/github.com/ethereum/go-ethereum/rlp/decode.go +++ b/vendor/github.com/ethereum/go-ethereum/rlp/decode.go @@ -55,81 +55,23 @@ var ( } ) -// Decoder is implemented by types that require custom RLP -// decoding rules or need to decode into private fields. +// Decoder is implemented by types that require custom RLP decoding rules or need to decode +// into private fields. // -// The DecodeRLP method should read one value from the given -// Stream. It is not forbidden to read less or more, but it might -// be confusing. +// The DecodeRLP method should read one value from the given Stream. It is not forbidden to +// read less or more, but it might be confusing. type Decoder interface { DecodeRLP(*Stream) error } -// Decode parses RLP-encoded data from r and stores the result in the -// value pointed to by val. Val must be a non-nil pointer. If r does -// not implement ByteReader, Decode will do its own buffering. +// Decode parses RLP-encoded data from r and stores the result in the value pointed to by +// val. Please see package-level documentation for the decoding rules. Val must be a +// non-nil pointer. // -// Decode uses the following type-dependent decoding rules: +// If r does not implement ByteReader, Decode will do its own buffering. // -// If the type implements the Decoder interface, decode calls -// DecodeRLP. -// -// To decode into a pointer, Decode will decode into the value pointed -// to. If the pointer is nil, a new value of the pointer's element -// type is allocated. If the pointer is non-nil, the existing value -// will be reused. -// -// To decode into a struct, Decode expects the input to be an RLP -// list. The decoded elements of the list are assigned to each public -// field in the order given by the struct's definition. The input list -// must contain an element for each decoded field. Decode returns an -// error if there are too few or too many elements. -// -// The decoding of struct fields honours certain struct tags, "tail", -// "nil" and "-". -// -// The "-" tag ignores fields. -// -// For an explanation of "tail", see the example. -// -// The "nil" tag applies to pointer-typed fields and changes the decoding -// rules for the field such that input values of size zero decode as a nil -// pointer. This tag can be useful when decoding recursive types. -// -// type StructWithEmptyOK struct { -// Foo *[20]byte `rlp:"nil"` -// } -// -// To decode into a slice, the input must be a list and the resulting -// slice will contain the input elements in order. For byte slices, -// the input must be an RLP string. Array types decode similarly, with -// the additional restriction that the number of input elements (or -// bytes) must match the array's length. -// -// To decode into a Go string, the input must be an RLP string. The -// input bytes are taken as-is and will not necessarily be valid UTF-8. -// -// To decode into an unsigned integer type, the input must also be an RLP -// string. The bytes are interpreted as a big endian representation of -// the integer. If the RLP string is larger than the bit size of the -// type, Decode will return an error. Decode also supports *big.Int. -// There is no size limit for big integers. -// -// To decode into a boolean, the input must contain an unsigned integer -// of value zero (false) or one (true). -// -// To decode into an interface value, Decode stores one of these -// in the value: -// -// []interface{}, for RLP lists -// []byte, for RLP strings -// -// Non-empty interface types are not supported, nor are signed integers, -// floating point numbers, maps, channels and functions. -// -// Note that Decode does not set an input limit for all readers -// and may be vulnerable to panics cause by huge value sizes. If -// you need an input limit, use +// Note that Decode does not set an input limit for all readers and may be vulnerable to +// panics cause by huge value sizes. If you need an input limit, use // // NewStream(r, limit).Decode(val) func Decode(r io.Reader, val interface{}) error { @@ -140,9 +82,8 @@ func Decode(r io.Reader, val interface{}) error { return stream.Decode(val) } -// DecodeBytes parses RLP data from b into val. -// Please see the documentation of Decode for the decoding rules. -// The input must contain exactly one value and no trailing data. +// DecodeBytes parses RLP data from b into val. Please see package-level documentation for +// the decoding rules. The input must contain exactly one value and no trailing data. func DecodeBytes(b []byte, val interface{}) error { r := bytes.NewReader(b) @@ -211,14 +152,15 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { switch { case typ == rawValueType: return decodeRawValue, nil - case typ.Implements(decoderInterface): return decodeDecoder, nil - case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface): - return decodeDecoderNoPtr, nil case typ.AssignableTo(reflect.PtrTo(bigInt)): return decodeBigInt, nil case typ.AssignableTo(bigInt): return decodeBigIntNoPtr, nil + case kind == reflect.Ptr: + return makePtrDecoder(typ, tags) + case reflect.PtrTo(typ).Implements(decoderInterface): + return decodeDecoder, nil case isUint(kind): return decodeUint, nil case kind == reflect.Bool: @@ -229,11 +171,6 @@ func makeDecoder(typ reflect.Type, tags tags) (dec decoder, err error) { return makeListDecoder(typ, tags) case kind == reflect.Struct: return makeStructDecoder(typ) - case kind == reflect.Ptr: - if tags.nilOK { - return makeOptionalPtrDecoder(typ) - } - return makePtrDecoder(typ) case kind == reflect.Interface: return decodeInterface, nil default: @@ -448,6 +385,11 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) { if err != nil { return nil, err } + for _, f := range fields { + if f.info.decoderErr != nil { + return nil, structFieldError{typ, f.index, f.info.decoderErr} + } + } dec := func(s *Stream, val reflect.Value) (err error) { if _, err := s.List(); err != nil { return wrapStreamError(err, typ) @@ -465,15 +407,22 @@ func makeStructDecoder(typ reflect.Type) (decoder, error) { return dec, nil } -// makePtrDecoder creates a decoder that decodes into -// the pointer's element type. -func makePtrDecoder(typ reflect.Type) (decoder, error) { +// makePtrDecoder creates a decoder that decodes into the pointer's element type. +func makePtrDecoder(typ reflect.Type, tag tags) (decoder, error) { etype := typ.Elem() etypeinfo := cachedTypeInfo1(etype, tags{}) - if etypeinfo.decoderErr != nil { + switch { + case etypeinfo.decoderErr != nil: return nil, etypeinfo.decoderErr + case !tag.nilOK: + return makeSimplePtrDecoder(etype, etypeinfo), nil + default: + return makeNilPtrDecoder(etype, etypeinfo, tag.nilKind), nil } - dec := func(s *Stream, val reflect.Value) (err error) { +} + +func makeSimplePtrDecoder(etype reflect.Type, etypeinfo *typeinfo) decoder { + return func(s *Stream, val reflect.Value) (err error) { newval := val if val.IsNil() { newval = reflect.New(etype) @@ -483,30 +432,35 @@ func makePtrDecoder(typ reflect.Type) (decoder, error) { } return err } - return dec, nil } -// makeOptionalPtrDecoder creates a decoder that decodes empty values -// as nil. Non-empty values are decoded into a value of the element type, -// just like makePtrDecoder does. +// makeNilPtrDecoder creates a decoder that decodes empty values as nil. Non-empty +// values are decoded into a value of the element type, just like makePtrDecoder does. // // This decoder is used for pointer-typed struct fields with struct tag "nil". -func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) { - etype := typ.Elem() - etypeinfo := cachedTypeInfo1(etype, tags{}) - if etypeinfo.decoderErr != nil { - return nil, etypeinfo.decoderErr - } - dec := func(s *Stream, val reflect.Value) (err error) { +func makeNilPtrDecoder(etype reflect.Type, etypeinfo *typeinfo, nilKind Kind) decoder { + typ := reflect.PtrTo(etype) + nilPtr := reflect.Zero(typ) + return func(s *Stream, val reflect.Value) (err error) { kind, size, err := s.Kind() - if err != nil || size == 0 && kind != Byte { + if err != nil { + val.Set(nilPtr) + return wrapStreamError(err, typ) + } + // Handle empty values as a nil pointer. + if kind != Byte && size == 0 { + if kind != nilKind { + return &decodeError{ + msg: fmt.Sprintf("wrong kind of empty value (got %v, want %v)", kind, nilKind), + typ: typ, + } + } // rearm s.Kind. This is important because the input // position must advance to the next value even though // we don't read anything. s.kind = -1 - // set the pointer to nil. - val.Set(reflect.Zero(typ)) - return err + val.Set(nilPtr) + return nil } newval := val if val.IsNil() { @@ -517,7 +471,6 @@ func makeOptionalPtrDecoder(typ reflect.Type) (decoder, error) { } return err } - return dec, nil } var ifsliceType = reflect.TypeOf([]interface{}{}) @@ -546,21 +499,8 @@ func decodeInterface(s *Stream, val reflect.Value) error { return nil } -// This decoder is used for non-pointer values of types -// that implement the Decoder interface using a pointer receiver. -func decodeDecoderNoPtr(s *Stream, val reflect.Value) error { - return val.Addr().Interface().(Decoder).DecodeRLP(s) -} - func decodeDecoder(s *Stream, val reflect.Value) error { - // Decoder instances are not handled using the pointer rule if the type - // implements Decoder with pointer receiver (i.e. always) - // because it might handle empty values specially. - // We need to allocate one here in this case, like makePtrDecoder does. - if val.Kind() == reflect.Ptr && val.IsNil() { - val.Set(reflect.New(val.Type().Elem())) - } - return val.Interface().(Decoder).DecodeRLP(s) + return val.Addr().Interface().(Decoder).DecodeRLP(s) } // Kind represents the kind of value contained in an RLP stream. diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/doc.go b/vendor/github.com/ethereum/go-ethereum/rlp/doc.go index b3a81fe232..7e6ee85200 100644 --- a/vendor/github.com/ethereum/go-ethereum/rlp/doc.go +++ b/vendor/github.com/ethereum/go-ethereum/rlp/doc.go @@ -17,17 +17,114 @@ /* Package rlp implements the RLP serialization format. -The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily -nested arrays of binary data, and RLP is the main encoding method used -to serialize objects in Ethereum. The only purpose of RLP is to encode -structure; encoding specific atomic data types (eg. strings, ints, -floats) is left up to higher-order protocols; in Ethereum integers -must be represented in big endian binary form with no leading zeroes -(thus making the integer value zero equivalent to the empty byte -array). - -RLP values are distinguished by a type tag. The type tag precedes the -value in the input stream and defines the size and kind of the bytes -that follow. +The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily nested arrays of +binary data, and RLP is the main encoding method used to serialize objects in Ethereum. +The only purpose of RLP is to encode structure; encoding specific atomic data types (eg. +strings, ints, floats) is left up to higher-order protocols. In Ethereum integers must be +represented in big endian binary form with no leading zeroes (thus making the integer +value zero equivalent to the empty string). + +RLP values are distinguished by a type tag. The type tag precedes the value in the input +stream and defines the size and kind of the bytes that follow. + + +Encoding Rules + +Package rlp uses reflection and encodes RLP based on the Go type of the value. + +If the type implements the Encoder interface, Encode calls EncodeRLP. It does not +call EncodeRLP on nil pointer values. + +To encode a pointer, the value being pointed to is encoded. A nil pointer to a struct +type, slice or array always encodes as an empty RLP list unless the slice or array has +elememt type byte. A nil pointer to any other value encodes as the empty string. + +Struct values are encoded as an RLP list of all their encoded public fields. Recursive +struct types are supported. + +To encode slices and arrays, the elements are encoded as an RLP list of the value's +elements. Note that arrays and slices with element type uint8 or byte are always encoded +as an RLP string. + +A Go string is encoded as an RLP string. + +An unsigned integer value is encoded as an RLP string. Zero always encodes as an empty RLP +string. big.Int values are treated as integers. Signed integers (int, int8, int16, ...) +are not supported and will return an error when encoding. + +Boolean values are encoded as the unsigned integers zero (false) and one (true). + +An interface value encodes as the value contained in the interface. + +Floating point numbers, maps, channels and functions are not supported. + + +Decoding Rules + +Decoding uses the following type-dependent rules: + +If the type implements the Decoder interface, DecodeRLP is called. + +To decode into a pointer, the value will be decoded as the element type of the pointer. If +the pointer is nil, a new value of the pointer's element type is allocated. If the pointer +is non-nil, the existing value will be reused. Note that package rlp never leaves a +pointer-type struct field as nil unless one of the "nil" struct tags is present. + +To decode into a struct, decoding expects the input to be an RLP list. The decoded +elements of the list are assigned to each public field in the order given by the struct's +definition. The input list must contain an element for each decoded field. Decoding +returns an error if there are too few or too many elements for the struct. + +To decode into a slice, the input must be a list and the resulting slice will contain the +input elements in order. For byte slices, the input must be an RLP string. Array types +decode similarly, with the additional restriction that the number of input elements (or +bytes) must match the array's defined length. + +To decode into a Go string, the input must be an RLP string. The input bytes are taken +as-is and will not necessarily be valid UTF-8. + +To decode into an unsigned integer type, the input must also be an RLP string. The bytes +are interpreted as a big endian representation of the integer. If the RLP string is larger +than the bit size of the type, decoding will return an error. Decode also supports +*big.Int. There is no size limit for big integers. + +To decode into a boolean, the input must contain an unsigned integer of value zero (false) +or one (true). + +To decode into an interface value, one of these types is stored in the value: + + []interface{}, for RLP lists + []byte, for RLP strings + +Non-empty interface types are not supported when decoding. +Signed integers, floating point numbers, maps, channels and functions cannot be decoded into. + + +Struct Tags + +Package rlp honours certain struct tags: "-", "tail", "nil", "nilList" and "nilString". + +The "-" tag ignores fields. + +The "tail" tag, which may only be used on the last exported struct field, allows slurping +up any excess list elements into a slice. See examples for more details. + +The "nil" tag applies to pointer-typed fields and changes the decoding rules for the field +such that input values of size zero decode as a nil pointer. This tag can be useful when +decoding recursive types. + + type StructWithOptionalFoo struct { + Foo *[20]byte `rlp:"nil"` + } + +RLP supports two kinds of empty values: empty lists and empty strings. When using the +"nil" tag, the kind of empty value allowed for a type is chosen automatically. A struct +field whose Go type is a pointer to an unsigned integer, string, boolean or byte +array/slice expects an empty RLP string. Any other pointer field type encodes/decodes as +an empty RLP list. + +The choice of null value can be made explicit with the "nilList" and "nilString" struct +tags. Using these tags encodes/decodes a Go nil pointer value as the kind of empty +RLP value defined by the tag. */ package rlp diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/encode.go b/vendor/github.com/ethereum/go-ethereum/rlp/encode.go index f255c38a9c..9c9e8d706d 100644 --- a/vendor/github.com/ethereum/go-ethereum/rlp/encode.go +++ b/vendor/github.com/ethereum/go-ethereum/rlp/encode.go @@ -49,36 +49,7 @@ type Encoder interface { // perform many small writes in some cases. Consider making w // buffered. // -// Encode uses the following type-dependent encoding rules: -// -// If the type implements the Encoder interface, Encode calls -// EncodeRLP. This is true even for nil pointers, please see the -// documentation for Encoder. -// -// To encode a pointer, the value being pointed to is encoded. For nil -// pointers, Encode will encode the zero value of the type. A nil -// pointer to a struct type always encodes as an empty RLP list. -// A nil pointer to an array encodes as an empty list (or empty string -// if the array has element type byte). -// -// Struct values are encoded as an RLP list of all their encoded -// public fields. Recursive struct types are supported. -// -// To encode slices and arrays, the elements are encoded as an RLP -// list of the value's elements. Note that arrays and slices with -// element type uint8 or byte are always encoded as an RLP string. -// -// A Go string is encoded as an RLP string. -// -// An unsigned integer value is encoded as an RLP string. Zero always -// encodes as an empty RLP string. Encode also supports *big.Int. -// -// Boolean values are encoded as unsigned integers zero (false) and one (true). -// -// An interface value encodes as the value contained in the interface. -// -// Signed integers are not supported, nor are floating point numbers, maps, -// channels and functions. +// Please see package-level documentation of encoding rules. func Encode(w io.Writer, val interface{}) error { if outer, ok := w.(*encbuf); ok { // Encode was called by some type's EncodeRLP. @@ -95,7 +66,7 @@ func Encode(w io.Writer, val interface{}) error { } // EncodeToBytes returns the RLP encoding of val. -// Please see the documentation of Encode for the encoding rules. +// Please see package-level documentation for the encoding rules. func EncodeToBytes(val interface{}) ([]byte, error) { eb := encbufPool.Get().(*encbuf) defer encbufPool.Put(eb) @@ -349,16 +320,14 @@ func makeWriter(typ reflect.Type, ts tags) (writer, error) { switch { case typ == rawValueType: return writeRawValue, nil - case typ.Implements(encoderInterface): - return writeEncoder, nil - case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(encoderInterface): - return writeEncoderNoPtr, nil - case kind == reflect.Interface: - return writeInterface, nil case typ.AssignableTo(reflect.PtrTo(bigInt)): return writeBigIntPtr, nil case typ.AssignableTo(bigInt): return writeBigIntNoPtr, nil + case kind == reflect.Ptr: + return makePtrWriter(typ, ts) + case reflect.PtrTo(typ).Implements(encoderInterface): + return makeEncoderWriter(typ), nil case isUint(kind): return writeUint, nil case kind == reflect.Bool: @@ -373,8 +342,8 @@ func makeWriter(typ reflect.Type, ts tags) (writer, error) { return makeSliceWriter(typ, ts) case kind == reflect.Struct: return makeStructWriter(typ) - case kind == reflect.Ptr: - return makePtrWriter(typ) + case kind == reflect.Interface: + return writeInterface, nil default: return nil, fmt.Errorf("rlp: type %v is not RLP-serializable", typ) } @@ -470,26 +439,6 @@ func writeString(val reflect.Value, w *encbuf) error { return nil } -func writeEncoder(val reflect.Value, w *encbuf) error { - return val.Interface().(Encoder).EncodeRLP(w) -} - -// writeEncoderNoPtr handles non-pointer values that implement Encoder -// with a pointer receiver. -func writeEncoderNoPtr(val reflect.Value, w *encbuf) error { - if !val.CanAddr() { - // We can't get the address. It would be possible to make the - // value addressable by creating a shallow copy, but this - // creates other problems so we're not doing it (yet). - // - // package json simply doesn't call MarshalJSON for cases like - // this, but encodes the value as if it didn't implement the - // interface. We don't want to handle it that way. - return fmt.Errorf("rlp: game over: unadressable value of type %v, EncodeRLP is pointer method", val.Type()) - } - return val.Addr().Interface().(Encoder).EncodeRLP(w) -} - func writeInterface(val reflect.Value, w *encbuf) error { if val.IsNil() { // Write empty list. This is consistent with the previous RLP @@ -531,6 +480,11 @@ func makeStructWriter(typ reflect.Type) (writer, error) { if err != nil { return nil, err } + for _, f := range fields { + if f.info.writerErr != nil { + return nil, structFieldError{typ, f.index, f.info.writerErr} + } + } writer := func(val reflect.Value, w *encbuf) error { lh := w.list() for _, f := range fields { @@ -544,44 +498,51 @@ func makeStructWriter(typ reflect.Type) (writer, error) { return writer, nil } -func makePtrWriter(typ reflect.Type) (writer, error) { +func makePtrWriter(typ reflect.Type, ts tags) (writer, error) { etypeinfo := cachedTypeInfo1(typ.Elem(), tags{}) if etypeinfo.writerErr != nil { return nil, etypeinfo.writerErr } - - // determine nil pointer handler - var nilfunc func(*encbuf) error - kind := typ.Elem().Kind() - switch { - case kind == reflect.Array && isByte(typ.Elem().Elem()): - nilfunc = func(w *encbuf) error { - w.str = append(w.str, 0x80) - return nil - } - case kind == reflect.Struct || kind == reflect.Array: - nilfunc = func(w *encbuf) error { - // encoding the zero value of a struct/array could trigger - // infinite recursion, avoid that. - w.listEnd(w.list()) - return nil - } - default: - zero := reflect.Zero(typ.Elem()) - nilfunc = func(w *encbuf) error { - return etypeinfo.writer(zero, w) - } + // Determine how to encode nil pointers. + var nilKind Kind + if ts.nilOK { + nilKind = ts.nilKind // use struct tag if provided + } else { + nilKind = defaultNilKind(typ.Elem()) } writer := func(val reflect.Value, w *encbuf) error { if val.IsNil() { - return nilfunc(w) + if nilKind == String { + w.str = append(w.str, 0x80) + } else { + w.listEnd(w.list()) + } + return nil } return etypeinfo.writer(val.Elem(), w) } return writer, nil } +func makeEncoderWriter(typ reflect.Type) writer { + if typ.Implements(encoderInterface) { + return func(val reflect.Value, w *encbuf) error { + return val.Interface().(Encoder).EncodeRLP(w) + } + } + w := func(val reflect.Value, w *encbuf) error { + if !val.CanAddr() { + // package json simply doesn't call MarshalJSON for this case, but encodes the + // value as if it didn't implement the interface. We don't want to handle it that + // way. + return fmt.Errorf("rlp: unadressable value of type %v, EncodeRLP is pointer method", val.Type()) + } + return val.Addr().Interface().(Encoder).EncodeRLP(w) + } + return w +} + // putint writes i to the beginning of b in big endian byte // order, using the least number of bytes needed to represent i. func putint(b []byte, i uint64) (size int) { diff --git a/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go b/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go index ab5ee3da76..e9a1e3f9e2 100644 --- a/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go +++ b/vendor/github.com/ethereum/go-ethereum/rlp/typecache.go @@ -35,22 +35,28 @@ type typeinfo struct { writerErr error // error from makeWriter } -// represents struct tags +// tags represents struct tags. type tags struct { // rlp:"nil" controls whether empty input results in a nil pointer. nilOK bool + + // This controls whether nil pointers are encoded/decoded as empty strings + // or empty lists. + nilKind Kind + // rlp:"tail" controls whether this field swallows additional list // elements. It can only be set for the last field, which must be // of slice type. tail bool + // rlp:"-" ignores fields. ignored bool } +// typekey is the key of a type in typeCache. It includes the struct tags because +// they might generate a different decoder. type typekey struct { reflect.Type - // the key must include the struct tags because they - // might generate a different decoder. tags } @@ -120,6 +126,25 @@ func structFields(typ reflect.Type) (fields []field, err error) { return fields, nil } +type structFieldError struct { + typ reflect.Type + field int + err error +} + +func (e structFieldError) Error() string { + return fmt.Sprintf("%v (struct field %v.%s)", e.err, e.typ, e.typ.Field(e.field).Name) +} + +type structTagError struct { + typ reflect.Type + field, tag, err string +} + +func (e structTagError) Error() string { + return fmt.Sprintf("rlp: invalid struct tag %q for %v.%s (%s)", e.tag, e.typ, e.field, e.err) +} + func parseStructTag(typ reflect.Type, fi, lastPublic int) (tags, error) { f := typ.Field(fi) var ts tags @@ -128,15 +153,26 @@ func parseStructTag(typ reflect.Type, fi, lastPublic int) (tags, error) { case "": case "-": ts.ignored = true - case "nil": + case "nil", "nilString", "nilList": ts.nilOK = true + if f.Type.Kind() != reflect.Ptr { + return ts, structTagError{typ, f.Name, t, "field is not a pointer"} + } + switch t { + case "nil": + ts.nilKind = defaultNilKind(f.Type.Elem()) + case "nilString": + ts.nilKind = String + case "nilList": + ts.nilKind = List + } case "tail": ts.tail = true if fi != lastPublic { - return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (must be on last field)`, typ, f.Name) + return ts, structTagError{typ, f.Name, t, "must be on last field"} } if f.Type.Kind() != reflect.Slice { - return ts, fmt.Errorf(`rlp: invalid struct tag "tail" for %v.%s (field type is not slice)`, typ, f.Name) + return ts, structTagError{typ, f.Name, t, "field type is not slice"} } default: return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name) @@ -160,6 +196,20 @@ func (i *typeinfo) generate(typ reflect.Type, tags tags) { i.writer, i.writerErr = makeWriter(typ, tags) } +// defaultNilKind determines whether a nil pointer to typ encodes/decodes +// as an empty string or empty list. +func defaultNilKind(typ reflect.Type) Kind { + k := typ.Kind() + if isUint(k) || k == reflect.String || k == reflect.Bool || isByteArray(typ) { + return String + } + return List +} + func isUint(k reflect.Kind) bool { return k >= reflect.Uint && k <= reflect.Uintptr } + +func isByteArray(typ reflect.Type) bool { + return (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array) && isByte(typ.Elem()) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/gzip.go b/vendor/github.com/ethereum/go-ethereum/rpc/gzip.go new file mode 100644 index 0000000000..a14fd09d54 --- /dev/null +++ b/vendor/github.com/ethereum/go-ethereum/rpc/gzip.go @@ -0,0 +1,66 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "compress/gzip" + "io" + "io/ioutil" + "net/http" + "strings" + "sync" +) + +var gzPool = sync.Pool{ + New: func() interface{} { + w := gzip.NewWriter(ioutil.Discard) + return w + }, +} + +type gzipResponseWriter struct { + io.Writer + http.ResponseWriter +} + +func (w *gzipResponseWriter) WriteHeader(status int) { + w.Header().Del("Content-Length") + w.ResponseWriter.WriteHeader(status) +} + +func (w *gzipResponseWriter) Write(b []byte) (int, error) { + return w.Writer.Write(b) +} + +func newGzipHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { + next.ServeHTTP(w, r) + return + } + + w.Header().Set("Content-Encoding", "gzip") + + gz := gzPool.Get().(*gzip.Writer) + defer gzPool.Put(gz) + + gz.Reset(w) + defer gz.Close() + + next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, Writer: gz}, r) + }) +} diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/http.go b/vendor/github.com/ethereum/go-ethereum/rpc/http.go index e8f2cfda77..2c0cb5edbe 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/http.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/http.go @@ -216,6 +216,7 @@ func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv ht // Wrap the CORS-handler within a host-handler handler := newCorsHandler(srv, cors) handler = newVHostHandler(vhosts, handler) + handler = newGzipHandler(handler) // Make sure timeout values are meaningful if timeouts.ReadTimeout < time.Second { diff --git a/vendor/github.com/ethereum/go-ethereum/rpc/types.go b/vendor/github.com/ethereum/go-ethereum/rpc/types.go index f31f09a774..e6b9f2a300 100644 --- a/vendor/github.com/ethereum/go-ethereum/rpc/types.go +++ b/vendor/github.com/ethereum/go-ethereum/rpc/types.go @@ -18,10 +18,12 @@ package rpc import ( "context" + "encoding/json" "fmt" "math" "strings" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ) @@ -105,3 +107,94 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { func (bn BlockNumber) Int64() int64 { return (int64)(bn) } + +type BlockNumberOrHash struct { + BlockNumber *BlockNumber `json:"blockNumber,omitempty"` + BlockHash *common.Hash `json:"blockHash,omitempty"` + RequireCanonical bool `json:"requireCanonical,omitempty"` +} + +func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { + type erased BlockNumberOrHash + e := erased{} + err := json.Unmarshal(data, &e) + if err == nil { + if e.BlockNumber != nil && e.BlockHash != nil { + return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other") + } + bnh.BlockNumber = e.BlockNumber + bnh.BlockHash = e.BlockHash + bnh.RequireCanonical = e.RequireCanonical + return nil + } + var input string + err = json.Unmarshal(data, &input) + if err != nil { + return err + } + switch input { + case "earliest": + bn := EarliestBlockNumber + bnh.BlockNumber = &bn + return nil + case "latest": + bn := LatestBlockNumber + bnh.BlockNumber = &bn + return nil + case "pending": + bn := PendingBlockNumber + bnh.BlockNumber = &bn + return nil + default: + if len(input) == 66 { + hash := common.Hash{} + err := hash.UnmarshalText([]byte(input)) + if err != nil { + return err + } + bnh.BlockHash = &hash + return nil + } else { + blckNum, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + if blckNum > math.MaxInt64 { + return fmt.Errorf("blocknumber too high") + } + bn := BlockNumber(blckNum) + bnh.BlockNumber = &bn + return nil + } + } +} + +func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) { + if bnh.BlockNumber != nil { + return *bnh.BlockNumber, true + } + return BlockNumber(0), false +} + +func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) { + if bnh.BlockHash != nil { + return *bnh.BlockHash, true + } + return common.Hash{}, false +} + +func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash { + return BlockNumberOrHash{ + BlockNumber: &blockNr, + BlockHash: nil, + RequireCanonical: false, + } +} + +func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash { + return BlockNumberOrHash{ + BlockNumber: nil, + BlockHash: &hash, + RequireCanonical: canonical, + } +} diff --git a/vendor/github.com/ethereum/go-ethereum/trie/sync.go b/vendor/github.com/ethereum/go-ethereum/trie/sync.go index 6f40b45a1e..e5a0c17493 100644 --- a/vendor/github.com/ethereum/go-ethereum/trie/sync.go +++ b/vendor/github.com/ethereum/go-ethereum/trie/sync.go @@ -57,14 +57,12 @@ type SyncResult struct { // persisted data items. type syncMemBatch struct { batch map[common.Hash][]byte // In-memory membatch of recently completed items - order []common.Hash // Order of completion to prevent out-of-order data loss } // newSyncMemBatch allocates a new memory-buffer for not-yet persisted trie nodes. func newSyncMemBatch() *syncMemBatch { return &syncMemBatch{ batch: make(map[common.Hash][]byte), - order: make([]common.Hash, 0, 256), } } @@ -223,20 +221,18 @@ func (s *Sync) Process(results []SyncResult) (bool, int, error) { } // Commit flushes the data stored in the internal membatch out to persistent -// storage, returning the number of items written and any occurred error. -func (s *Sync) Commit(dbw ethdb.KeyValueWriter) (int, error) { +// storage, returning any occurred error. +func (s *Sync) Commit(dbw ethdb.Batch) error { // Dump the membatch into a database dbw - for i, key := range s.membatch.order { - if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil { - return i, err + for key, value := range s.membatch.batch { + if err := dbw.Put(key[:], value); err != nil { + return err } s.bloom.Add(key[:]) } - written := len(s.membatch.order) // TODO(karalabe): could an order change improve write performance? - // Drop the membatch data and return s.membatch = newSyncMemBatch() - return written, nil + return nil } // Pending returns the number of state entries currently pending for download. @@ -330,7 +326,6 @@ func (s *Sync) children(req *request, object node) ([]*request, error) { func (s *Sync) commit(req *request) (err error) { // Write the node content to the membatch s.membatch.batch[req.hash] = req.data - s.membatch.order = append(s.membatch.order, req.hash) delete(s.requests, req.hash) diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/doc.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/doc.go index 529bf3d2de..44c0c3271c 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/doc.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/doc.go @@ -34,6 +34,8 @@ package whisperv6 import ( "time" + + "github.com/ethereum/go-ethereum/crypto" ) // Whisper protocol parameters @@ -54,12 +56,12 @@ const ( SizeMask = byte(3) // mask used to extract the size of payload size field from the flags signatureFlag = byte(4) - TopicLength = 4 // in bytes - signatureLength = 65 // in bytes - aesKeyLength = 32 // in bytes - aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() - keyIDSize = 32 // in bytes - BloomFilterSize = 64 // in bytes + TopicLength = 4 // in bytes + signatureLength = crypto.SignatureLength // in bytes + aesKeyLength = 32 // in bytes + aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() + keyIDSize = 32 // in bytes + BloomFilterSize = 64 // in bytes flagsLength = 1 EnvelopeHeaderLength = 20 diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/batch.go b/vendor/github.com/syndtr/goleveldb/leveldb/batch.go index 225920002d..823be93f93 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/batch.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/batch.go @@ -238,6 +238,11 @@ func newBatch() interface{} { return &Batch{} } +// MakeBatch returns empty batch with preallocated buffer. +func MakeBatch(n int) *Batch { + return &Batch{data: make([]byte, 0, n)} +} + func decodeBatch(data []byte, fn func(i int, index batchIndex) error) error { var index batchIndex for i, o := 0, 0; o < len(data); i++ { diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db.go b/vendor/github.com/syndtr/goleveldb/leveldb/db.go index 0de5ffe8d7..74e9826956 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db.go @@ -38,6 +38,12 @@ type DB struct { inWritePaused int32 // The indicator whether write operation is paused by compaction aliveSnaps, aliveIters int32 + // Compaction statistic + memComp uint32 // The cumulative number of memory compaction + level0Comp uint32 // The cumulative number of level0 compaction + nonLevel0Comp uint32 // The cumulative number of non-level0 compaction + seekComp uint32 // The cumulative number of seek compaction + // Session. s *session @@ -978,6 +984,8 @@ func (db *DB) GetProperty(name string) (value string, err error) { value += fmt.Sprintf(" Total | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", totalTables, float64(totalSize)/1048576.0, totalDuration.Seconds(), float64(totalRead)/1048576.0, float64(totalWrite)/1048576.0) + case p == "compcount": + value = fmt.Sprintf("MemComp:%d Level0Comp:%d NonLevel0Comp:%d SeekComp:%d", atomic.LoadUint32(&db.memComp), atomic.LoadUint32(&db.level0Comp), atomic.LoadUint32(&db.nonLevel0Comp), atomic.LoadUint32(&db.seekComp)) case p == "iostats": value = fmt.Sprintf("Read(MB):%.5f Write(MB):%.5f", float64(db.s.stor.reads())/1048576.0, @@ -1034,6 +1042,11 @@ type DBStats struct { LevelRead Sizes LevelWrite Sizes LevelDurations []time.Duration + + MemComp uint32 + Level0Comp uint32 + NonLevel0Comp uint32 + SeekComp uint32 } // Stats populates s with database statistics. @@ -1070,16 +1083,17 @@ func (db *DB) Stats(s *DBStats) error { for level, tables := range v.levels { duration, read, write := db.compStats.getStat(level) - if len(tables) == 0 && duration == 0 { - continue - } + s.LevelDurations = append(s.LevelDurations, duration) s.LevelRead = append(s.LevelRead, read) s.LevelWrite = append(s.LevelWrite, write) s.LevelSizes = append(s.LevelSizes, tables.size()) s.LevelTablesCounts = append(s.LevelTablesCounts, len(tables)) } - + s.MemComp = atomic.LoadUint32(&db.memComp) + s.Level0Comp = atomic.LoadUint32(&db.level0Comp) + s.NonLevel0Comp = atomic.LoadUint32(&db.nonLevel0Comp) + s.SeekComp = atomic.LoadUint32(&db.seekComp) return nil } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go b/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go index 56f3632a7d..6b70eb2c9d 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db_compaction.go @@ -8,6 +8,7 @@ package leveldb import ( "sync" + "sync/atomic" "time" "github.com/syndtr/goleveldb/leveldb/errors" @@ -324,10 +325,12 @@ func (db *DB) memCompaction() { db.logf("memdb@flush committed F·%d T·%v", len(rec.addedTables), stats.duration) + // Save compaction stats for _, r := range rec.addedTables { stats.write += r.size } db.compStats.addStat(flushLevel, stats) + atomic.AddUint32(&db.memComp, 1) // Drop frozen memdb. db.dropFrozenMem() @@ -588,6 +591,14 @@ func (db *DB) tableCompaction(c *compaction, noTrivial bool) { for i := range stats { db.compStats.addStat(c.sourceLevel+1, &stats[i]) } + switch c.typ { + case level0Compaction: + atomic.AddUint32(&db.level0Comp, 1) + case nonLevel0Compaction: + atomic.AddUint32(&db.nonLevel0Comp, 1) + case seekCompaction: + atomic.AddUint32(&db.seekComp, 1) + } } func (db *DB) tableRangeCompaction(level int, umin, umax []byte) error { diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go b/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go index 03c24cdab5..e6e8ca59d0 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db_iter.go @@ -78,13 +78,17 @@ func (db *DB) newIterator(auxm *memDB, auxt tFiles, seq uint64, slice *util.Rang } rawIter := db.newRawIterator(auxm, auxt, islice, ro) iter := &dbIter{ - db: db, - icmp: db.s.icmp, - iter: rawIter, - seq: seq, - strict: opt.GetStrict(db.s.o.Options, ro, opt.StrictReader), - key: make([]byte, 0), - value: make([]byte, 0), + db: db, + icmp: db.s.icmp, + iter: rawIter, + seq: seq, + strict: opt.GetStrict(db.s.o.Options, ro, opt.StrictReader), + disableSampling: db.s.o.GetDisableSeeksCompaction() || db.s.o.GetIteratorSamplingRate() <= 0, + key: make([]byte, 0), + value: make([]byte, 0), + } + if !iter.disableSampling { + iter.samplingGap = db.iterSamplingRate() } atomic.AddInt32(&db.aliveIters, 1) runtime.SetFinalizer(iter, (*dbIter).Release) @@ -107,13 +111,14 @@ const ( // dbIter represent an interator states over a database session. type dbIter struct { - db *DB - icmp *iComparer - iter iterator.Iterator - seq uint64 - strict bool - - smaplingGap int + db *DB + icmp *iComparer + iter iterator.Iterator + seq uint64 + strict bool + disableSampling bool + + samplingGap int dir dir key []byte value []byte @@ -122,10 +127,14 @@ type dbIter struct { } func (i *dbIter) sampleSeek() { + if i.disableSampling { + return + } + ikey := i.iter.Key() - i.smaplingGap -= len(ikey) + len(i.iter.Value()) - for i.smaplingGap < 0 { - i.smaplingGap += i.db.iterSamplingRate() + i.samplingGap -= len(ikey) + len(i.iter.Value()) + for i.samplingGap < 0 { + i.samplingGap += i.db.iterSamplingRate() i.db.sampleSeek(ikey) } } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go b/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go index f145b64fbb..21d1e512f3 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/db_transaction.go @@ -69,6 +69,9 @@ func (tr *Transaction) Has(key []byte, ro *opt.ReadOptions) (bool, error) { // DB. And a nil Range.Limit is treated as a key after all keys in // the DB. // +// The returned iterator has locks on its own resources, so it can live beyond +// the lifetime of the transaction who creates them. +// // WARNING: Any slice returned by interator (e.g. slice returned by calling // Iterator.Key() or Iterator.Key() methods), its content should not be modified // unless noted otherwise. @@ -252,13 +255,14 @@ func (tr *Transaction) discard() { // Discard transaction. for _, t := range tr.tables { tr.db.logf("transaction@discard @%d", t.fd.Num) - if err1 := tr.db.s.stor.Remove(t.fd); err1 == nil { - tr.db.s.reuseFileNum(t.fd.Num) - } + // Iterator may still use the table, so we use tOps.remove here. + tr.db.s.tops.remove(t.fd) } } // Discard discards the transaction. +// This method is noop if transaction is already closed (either committed or +// discarded) // // Other methods should not be called after transaction has been discarded. func (tr *Transaction) Discard() { @@ -282,8 +286,10 @@ func (db *DB) waitCompaction() error { // until in-flight transaction is committed or discarded. // The returned transaction handle is safe for concurrent use. // -// Transaction is expensive and can overwhelm compaction, especially if +// Transaction is very expensive and can overwhelm compaction, especially if // transaction size is small. Use with caution. +// The rule of thumb is if you need to merge at least same amount of +// `Options.WriteBuffer` worth of data then use transaction, otherwise don't. // // The transaction must be closed once done, either by committing or discarding // the transaction. diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go b/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go index 528b164233..c02c1e9788 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/opt/options.go @@ -278,6 +278,14 @@ type Options struct { // The default is false. DisableLargeBatchTransaction bool + // DisableSeeksCompaction allows disabling 'seeks triggered compaction'. + // The purpose of 'seeks triggered compaction' is to optimize database so + // that 'level seeks' can be minimized, however this might generate many + // small compaction which may not preferable. + // + // The default is false. + DisableSeeksCompaction bool + // ErrorIfExist defines whether an error should returned if the DB already // exist. // @@ -309,6 +317,8 @@ type Options struct { // IteratorSamplingRate defines approximate gap (in bytes) between read // sampling of an iterator. The samples will be used to determine when // compaction should be triggered. + // Use negative value to disable iterator sampling. + // The iterator sampling is disabled if DisableSeeksCompaction is true. // // The default is 1MiB. IteratorSamplingRate int @@ -526,6 +536,13 @@ func (o *Options) GetDisableLargeBatchTransaction() bool { return o.DisableLargeBatchTransaction } +func (o *Options) GetDisableSeeksCompaction() bool { + if o == nil { + return false + } + return o.DisableSeeksCompaction +} + func (o *Options) GetErrorIfExist() bool { if o == nil { return false @@ -548,8 +565,10 @@ func (o *Options) GetFilter() filter.Filter { } func (o *Options) GetIteratorSamplingRate() int { - if o == nil || o.IteratorSamplingRate <= 0 { + if o == nil || o.IteratorSamplingRate == 0 { return DefaultIteratorSamplingRate + } else if o.IteratorSamplingRate < 0 { + return 0 } return o.IteratorSamplingRate } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go b/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go index f6030022de..4c1d336bef 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/session_compaction.go @@ -14,6 +14,13 @@ import ( "github.com/syndtr/goleveldb/leveldb/opt" ) +const ( + undefinedCompaction = iota + level0Compaction + nonLevel0Compaction + seekCompaction +) + func (s *session) pickMemdbLevel(umin, umax []byte, maxLevel int) int { v := s.version() defer v.release() @@ -50,6 +57,7 @@ func (s *session) pickCompaction() *compaction { var sourceLevel int var t0 tFiles + var typ int if v.cScore >= 1 { sourceLevel = v.cLevel cptr := s.getCompPtr(sourceLevel) @@ -63,18 +71,24 @@ func (s *session) pickCompaction() *compaction { if len(t0) == 0 { t0 = append(t0, tables[0]) } + if sourceLevel == 0 { + typ = level0Compaction + } else { + typ = nonLevel0Compaction + } } else { if p := atomic.LoadPointer(&v.cSeek); p != nil { ts := (*tSet)(p) sourceLevel = ts.level t0 = append(t0, ts.table) + typ = seekCompaction } else { v.release() return nil } } - return newCompaction(s, v, sourceLevel, t0) + return newCompaction(s, v, sourceLevel, t0, typ) } // Create compaction from given level and range; need external synchronization. @@ -109,13 +123,18 @@ func (s *session) getCompactionRange(sourceLevel int, umin, umax []byte, noLimit } } - return newCompaction(s, v, sourceLevel, t0) + typ := level0Compaction + if sourceLevel != 0 { + typ = nonLevel0Compaction + } + return newCompaction(s, v, sourceLevel, t0, typ) } -func newCompaction(s *session, v *version, sourceLevel int, t0 tFiles) *compaction { +func newCompaction(s *session, v *version, sourceLevel int, t0 tFiles, typ int) *compaction { c := &compaction{ s: s, v: v, + typ: typ, sourceLevel: sourceLevel, levels: [2]tFiles{t0, nil}, maxGPOverlaps: int64(s.o.GetCompactionGPOverlaps(sourceLevel)), @@ -131,6 +150,7 @@ type compaction struct { s *session v *version + typ int sourceLevel int levels [2]tFiles maxGPOverlaps int64 diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go b/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go index 67a6df9790..fc56b631f0 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/session_util.go @@ -308,7 +308,7 @@ func (s *session) setNextFileNum(num int64) { func (s *session) markFileNum(num int64) { nextFileNum := num + 1 for { - old, x := s.stNextFileNum, nextFileNum + old, x := atomic.LoadInt64(&s.stNextFileNum), nextFileNum if old > x { x = old } @@ -326,7 +326,7 @@ func (s *session) allocFileNum() int64 { // Reuse given file number. func (s *session) reuseFileNum(num int64) { for { - old, x := s.stNextFileNum, num + old, x := atomic.LoadInt64(&s.stNextFileNum), num if old != x+1 { x = old } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/table.go b/vendor/github.com/syndtr/goleveldb/leveldb/table.go index 5ad1f80543..b7759b2f5c 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/table.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/table.go @@ -493,6 +493,8 @@ func (t *tOps) remove(fd storage.FileDesc) { if t.evictRemoved && t.bcache != nil { t.bcache.EvictNS(uint64(fd.Num)) } + // Try to reuse file num, useful for discarded transaction. + t.s.reuseFileNum(fd.Num) }) } diff --git a/vendor/github.com/syndtr/goleveldb/leveldb/version.go b/vendor/github.com/syndtr/goleveldb/leveldb/version.go index 2664560e1c..9535e35914 100644 --- a/vendor/github.com/syndtr/goleveldb/leveldb/version.go +++ b/vendor/github.com/syndtr/goleveldb/leveldb/version.go @@ -144,6 +144,7 @@ func (v *version) get(aux tFiles, ikey internalKey, ro *opt.ReadOptions, noValue } ukey := ikey.ukey() + sampleSeeks := !v.s.o.GetDisableSeeksCompaction() var ( tset *tSet @@ -161,7 +162,7 @@ func (v *version) get(aux tFiles, ikey internalKey, ro *opt.ReadOptions, noValue // Since entries never hop across level, finding key/value // in smaller level make later levels irrelevant. v.walkOverlapping(aux, ikey, func(level int, t *tFile) bool { - if level >= 0 && !tseek { + if sampleSeeks && level >= 0 && !tseek { if tset == nil { tset = &tSet{level, t} } else { diff --git a/vendor/modules.txt b/vendor/modules.txt index 2da31c0059..0aea58b8a8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -88,7 +88,7 @@ github.com/edsrzf/mmap-go # github.com/elastic/gosigar v0.0.0-20180330100440-37f05ff46ffa github.com/elastic/gosigar github.com/elastic/gosigar/sys/windows -# github.com/ethereum/go-ethereum v1.9.2 +# github.com/ethereum/go-ethereum v1.9.7 github.com/ethereum/go-ethereum github.com/ethereum/go-ethereum/accounts github.com/ethereum/go-ethereum/accounts/abi @@ -122,6 +122,7 @@ github.com/ethereum/go-ethereum/core/state github.com/ethereum/go-ethereum/core/types github.com/ethereum/go-ethereum/core/vm github.com/ethereum/go-ethereum/crypto +github.com/ethereum/go-ethereum/crypto/blake2b github.com/ethereum/go-ethereum/crypto/bn256 github.com/ethereum/go-ethereum/crypto/bn256/cloudflare github.com/ethereum/go-ethereum/crypto/bn256/google @@ -325,7 +326,7 @@ github.com/status-im/keycard-go/derivationpath github.com/steakknife/bloomfilter # github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 github.com/steakknife/hamming -# github.com/syndtr/goleveldb v0.0.0-20190318030020-c3a204f8e965 +# github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d github.com/syndtr/goleveldb/leveldb github.com/syndtr/goleveldb/leveldb/cache github.com/syndtr/goleveldb/leveldb/comparer