Commit 4e1878e1 authored by jiangpeng's avatar jiangpeng Committed by vipwzw

plugin:import p2p plugin gossip

parent 298964c4
...@@ -46,14 +46,7 @@ enableReExecLocal=true ...@@ -46,14 +46,7 @@ enableReExecLocal=true
enableReduceLocaldb=false enableReduceLocaldb=false
[p2p] [p2p]
seeds=[]
enable=false enable=false
isSeed=false
serverStart=true
innerSeedEnable=true
useGithub=true
innerBounds=300
msgCacheSize=10240
driver="leveldb" driver="leveldb"
dbPath="paradatadir/addrbook" dbPath="paradatadir/addrbook"
dbCache=4 dbCache=4
......
...@@ -44,18 +44,37 @@ enableReExecLocal=true ...@@ -44,18 +44,37 @@ enableReExecLocal=true
enableReduceLocaldb=true enableReduceLocaldb=true
[p2p] [p2p]
seeds=[] # p2p类型
types=["gossip", "dht"]
# 是否启动P2P服务
enable=true enable=true
isSeed=false # 使用的数据库类型
serverStart=true
innerSeedEnable=true
useGithub=true
innerBounds=300
msgCacheSize=10240
driver="leveldb" driver="leveldb"
# 使用的数据库类型
dbPath="datadir/addrbook" dbPath="datadir/addrbook"
# 数据库缓存大小
dbCache=4 dbCache=4
# GRPC请求日志文件
grpcLogFile="grpc33.log" grpcLogFile="grpc33.log"
#waitPid 等待seed导入
waitPid=false
[p2p.sub.gossip]
# 种子节点,格式为ip:port,多个节点以逗号分隔,如seeds=["10.0.0.1:13802","10.0.0.2:13802","10.0.0.3:13802"]
seeds=[]
# 是否为种子节点
isSeed=false
# 是否作为服务端,对外提供服务
serverStart=true
# 是否使用Github获取种子节点
useGithub=true
# 最多的接入节点个数
innerBounds=300
[p2p.sub.dht]
seeds=[""]
[rpc] [rpc]
jrpcBindAddr="localhost:8801" jrpcBindAddr="localhost:8801"
...@@ -260,4 +279,4 @@ url="http://influxdb:8086" ...@@ -260,4 +279,4 @@ url="http://influxdb:8086"
database="chain33metrics" database="chain33metrics"
username="" username=""
password="" password=""
namespace="" namespace=""
\ No newline at end of file
...@@ -2,31 +2,30 @@ module github.com/33cn/plugin ...@@ -2,31 +2,30 @@ module github.com/33cn/plugin
go 1.12 go 1.12
replace github.com/33cn/chain33 => ../chain33
require ( require (
github.com/33cn/chain33 v0.0.0-20200311054608-67f804d0ad2c github.com/33cn/chain33 v0.0.0-20200311054608-67f804d0ad2c
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/NebulousLabs/Sia v1.3.7 github.com/NebulousLabs/Sia v1.3.7
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d // indirect
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
github.com/coreos/etcd v3.3.15+incompatible github.com/coreos/etcd v3.3.15+incompatible
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/protobuf v1.3.4 github.com/golang/protobuf v1.3.4
github.com/hashicorp/golang-lru v0.5.0 github.com/hashicorp/golang-lru v0.5.3
github.com/pkg/errors v0.8.0 github.com/huin/goupnp v1.0.0
github.com/jackpal/go-nat-pmp v1.0.1
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.2 // indirect github.com/prometheus/client_golang v0.9.2 // indirect
github.com/prometheus/common v0.4.1 // indirect github.com/prometheus/common v0.4.1 // indirect
github.com/prometheus/procfs v0.0.3 // indirect github.com/prometheus/procfs v0.0.3 // indirect
github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d
github.com/rs/cors v1.6.0 github.com/rs/cors v1.6.0
github.com/spf13/cobra v0.0.3 github.com/spf13/cobra v0.0.5
github.com/stretchr/testify v1.3.0 github.com/stretchr/testify v1.3.0
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8
github.com/valyala/fasthttp v1.5.0 github.com/valyala/fasthttp v1.5.0
...@@ -34,7 +33,7 @@ require ( ...@@ -34,7 +33,7 @@ require (
go.uber.org/atomic v1.4.0 // indirect go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.2.0 // indirect go.uber.org/multierr v1.2.0 // indirect
go.uber.org/zap v1.10.0 // indirect go.uber.org/zap v1.10.0 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392
golang.org/x/net v0.0.0-20200301022130-244492dfa37a golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 // indirect golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 // indirect
google.golang.org/grpc v1.28.0 google.golang.org/grpc v1.28.0
......
...@@ -7,8 +7,11 @@ github.com/33cn/chain33 v0.0.0-20200311054608-67f804d0ad2c h1:lsTHfmeWOswmBePSoL ...@@ -7,8 +7,11 @@ github.com/33cn/chain33 v0.0.0-20200311054608-67f804d0ad2c h1:lsTHfmeWOswmBePSoL
github.com/33cn/chain33 v0.0.0-20200311054608-67f804d0ad2c/go.mod h1:AVCk9yTfBhiLcYM4z4lLHdKeTnIR7GWiSXwDngPjs2w= github.com/33cn/chain33 v0.0.0-20200311054608-67f804d0ad2c/go.mod h1:AVCk9yTfBhiLcYM4z4lLHdKeTnIR7GWiSXwDngPjs2w=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 h1:PqzgE6kAMi81xWQA2QIVxjWkFHptGgC547vchpUbtFo=
github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/NebulousLabs/Sia v1.3.7 h1:gYfYnXVyeaEzyyVwjpQjszBcENNZ8DPIJc/pgOiLuGA= github.com/NebulousLabs/Sia v1.3.7 h1:gYfYnXVyeaEzyyVwjpQjszBcENNZ8DPIJc/pgOiLuGA=
github.com/NebulousLabs/Sia v1.3.7/go.mod h1:SCASk6mV8QdEojKyecjj/Jd0OGSXkZonkhow7XXKk6Q= github.com/NebulousLabs/Sia v1.3.7/go.mod h1:SCASk6mV8QdEojKyecjj/Jd0OGSXkZonkhow7XXKk6Q=
github.com/NebulousLabs/entropy-mnemonics v0.0.0-20170316012907-7b01a644a636 h1:8jJHuLVX2BpLA7ZelKeU14Uu+44nwlG8nCio188M04k= github.com/NebulousLabs/entropy-mnemonics v0.0.0-20170316012907-7b01a644a636 h1:8jJHuLVX2BpLA7ZelKeU14Uu+44nwlG8nCio188M04k=
...@@ -21,10 +24,12 @@ github.com/NebulousLabs/merkletree v0.0.0-20181025040823-2a1d1d1dc33c h1:PylSN1K ...@@ -21,10 +24,12 @@ github.com/NebulousLabs/merkletree v0.0.0-20181025040823-2a1d1d1dc33c h1:PylSN1K
github.com/NebulousLabs/merkletree v0.0.0-20181025040823-2a1d1d1dc33c/go.mod h1:Cn056wBLKay+uIS9LJn7ymwhgC5mqbOtG6iOhEvyy4M= github.com/NebulousLabs/merkletree v0.0.0-20181025040823-2a1d1d1dc33c/go.mod h1:Cn056wBLKay+uIS9LJn7ymwhgC5mqbOtG6iOhEvyy4M=
github.com/XiaoMi/pegasus-go-client v0.0.0-20181029071519-9400942c5d1c h1:3fAhdHMhoSG57DjJ/dqLFfgD+FoooPbQH6szINbrr3k= github.com/XiaoMi/pegasus-go-client v0.0.0-20181029071519-9400942c5d1c h1:3fAhdHMhoSG57DjJ/dqLFfgD+FoooPbQH6szINbrr3k=
github.com/XiaoMi/pegasus-go-client v0.0.0-20181029071519-9400942c5d1c/go.mod h1:KcL6D/4RZ8RAYzQ5gKI0odcdWUmCVlbQTOlWrhP71CY= github.com/XiaoMi/pegasus-go-client v0.0.0-20181029071519-9400942c5d1c/go.mod h1:KcL6D/4RZ8RAYzQ5gKI0odcdWUmCVlbQTOlWrhP71CY=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d h1:b/FqDLjWXDQI6XBYvWDVgEKv3xOTs68qRkuqyU37lBc= github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d h1:b/FqDLjWXDQI6XBYvWDVgEKv3xOTs68qRkuqyU37lBc=
github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
...@@ -32,27 +37,40 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= ...@@ -32,27 +37,40 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac h1:/zx+Hglw2JN/pwVam1Z8cTCTl4pWyrbvOn2oooqCQSs= github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac h1:/zx+Hglw2JN/pwVam1Z8cTCTl4pWyrbvOn2oooqCQSs=
github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20181013004428-67e573d211ac/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0=
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/bbolt v1.3.0 h1:HIgH5xUWXT914HCI671AxuTTqjj64UOFr7pHn48LUTI= github.com/coreos/bbolt v1.3.0 h1:HIgH5xUWXT914HCI671AxuTTqjj64UOFr7pHn48LUTI=
github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.15+incompatible h1:+9RjdC18gMxNQVvSiXvObLu29mOFmkgdsB4cRTlV+EE= github.com/coreos/etcd v3.3.15+incompatible h1:+9RjdC18gMxNQVvSiXvObLu29mOFmkgdsB4cRTlV+EE=
github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
...@@ -62,8 +80,16 @@ github.com/decred/base58 v1.0.0 h1:BVi1FQCThIjZ0ehG+I99NJ51o0xcc9A/fDKhmJxY6+w= ...@@ -62,8 +80,16 @@ github.com/decred/base58 v1.0.0 h1:BVi1FQCThIjZ0ehG+I99NJ51o0xcc9A/fDKhmJxY6+w=
github.com/decred/base58 v1.0.0/go.mod h1:LLY1p5e3g91byL/UO1eiZaYd+uRoVRarybgcoymu9Ks= github.com/decred/base58 v1.0.0/go.mod h1:LLY1p5e3g91byL/UO1eiZaYd+uRoVRarybgcoymu9Ks=
github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg= github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg=
github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ=
github.com/dgraph-io/badger v1.6.0-rc1 h1:JphPpoBZJ3WHha133BGYlQqltSGIhV+VsEID0++nN9A=
github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+34X//E8Wg3nEbMJxJkwSc0tPePK0= github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102 h1:afESQBXJEnj3fu+34X//E8Wg3nEbMJxJkwSc0tPePK0=
github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20180109070241-2de33835d102/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
...@@ -72,19 +98,26 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 ...@@ -72,19 +98,26 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
...@@ -94,10 +127,21 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l ...@@ -94,10 +127,21 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6 h1:HE4YDtvtpZgjRJ2tCOmaXlcpBTFG2e0jvfNntM5sXOs= github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6 h1:HE4YDtvtpZgjRJ2tCOmaXlcpBTFG2e0jvfNntM5sXOs=
github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6/go.mod h1:73mKQiY8bLnscfGakn57WAJZTzT0eSUAy3qgMQNR/DI= github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6/go.mod h1:73mKQiY8bLnscfGakn57WAJZTzT0eSUAy3qgMQNR/DI=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= 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.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/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=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
...@@ -107,36 +151,228 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH ...@@ -107,36 +151,228 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4= github.com/influxdata/influxdb v1.7.9 h1:uSeBTNO4rBkbp1Be5FKRsAmglM9nlx25TzVQRQt1An4=
github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.7.9/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.3 h1:UIAh32wymBpStoe83YCzwVQQ5Oy/H0FdxvUS6DJDzms=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-datastore v0.1.0 h1:TOxI04l8CmO4zGtesENhzm4PwkFwJXY3rKiYaaMf9fI=
github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=
github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc=
github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50=
github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=
github.com/ipfs/go-log v0.0.1 h1:9XTUN/rW64BCG1YhPK9Hoy3q8nr4gOmHHBpgFdfw6Lc=
github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
github.com/ipfs/go-todocounter v0.0.1 h1:kITWA5ZcQZfrUnDNkRn04Xzh0YFaDFXsoO2A81Eb6Lw=
github.com/ipfs/go-todocounter v0.0.1/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4=
github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc=
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA= github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA=
github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2 h1:vhC1OXXiT9R2pczegwz6moDvuRpggaroAXhPIseh57A=
github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=
github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10=
github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs= github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
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.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b h1:wxtKgYHEncAU00muMD06dzLiahtGM1eouRNOzVV7tdQ=
github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88=
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
github.com/libp2p/go-conn-security-multistream v0.1.0 h1:aqGmto+ttL/uJgX0JtQI0tD21CIEy5eYd1Hlp0juHY0=
github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc=
github.com/libp2p/go-eventbus v0.0.2/go.mod h1:Hr/yGlwxA/stuLnpMiu82lpNKpvRy3EaJxPu40XYOwk=
github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvMfzQ=
github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4=
github.com/libp2p/go-flow-metrics v0.0.1 h1:0gxuFd2GuK7IIP5pKljLwps6TvcuYgvG7Atqi3INF5s=
github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=
github.com/libp2p/go-libp2p v0.3.1/go.mod h1:e6bwxbdYH1HqWTz8faTChKGR0BjPc8p+6SyP8GTTR7Y=
github.com/libp2p/go-libp2p v0.4.0 h1:nV2q3fdhL80OWtPyBrsoWKcw32qC4TbbR+iGjEOMRaU=
github.com/libp2p/go-libp2p v0.4.0/go.mod h1:9EsEIf9p2UDuwtPd0DwJsAl0qXVxgAnuDGRvHbfATfI=
github.com/libp2p/go-libp2p-autonat v0.1.0 h1:aCWAu43Ri4nU0ZPO7NyLzUvvfqd0nE3dX0R/ZGYVgOU=
github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8=
github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro=
github.com/libp2p/go-libp2p-blankhost v0.1.3/go.mod h1:KML1//wiKR8vuuJO0y3LUd1uLv+tlkGTAr3jC0S5cLg=
github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU=
github.com/libp2p/go-libp2p-circuit v0.1.1/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8=
github.com/libp2p/go-libp2p-circuit v0.1.3 h1:WsMYYaA0PwdpgJSQu12EzPYf5ypkLSTgcOsWr7DYrgI=
github.com/libp2p/go-libp2p-circuit v0.1.3/go.mod h1:Xqh2TjSy8DD5iV2cCOMzdynd6h8OTBGoV1AWbWor3qM=
github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco=
github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I=
github.com/libp2p/go-libp2p-core v0.0.6/go.mod h1:0d9xmaYAVY5qmbp/fcgxHT3ZJsLjYeYPMJAUKpaCHrE=
github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI=
github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0=
github.com/libp2p/go-libp2p-core v0.2.3 h1:zXikZ5pLfebtTMeIYfcwVQ2Pae77O0FIwDquwM6AGNM=
github.com/libp2p/go-libp2p-core v0.2.3/go.mod h1:GqhyQqyIAPsxFYXHMjfXgMv03lxsvM0mFzuYA9Ib42A=
github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI=
github.com/libp2p/go-libp2p-discovery v0.1.0 h1:j+R6cokKcGbnZLf4kcNwpx6mDEUPF3N6SrqMymQhmvs=
github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g=
github.com/libp2p/go-libp2p-kad-dht v0.2.1 h1:+pb1DCkV/6oNQjTZVXl+Y++eV0rnelx/L8y1t4J+Rnw=
github.com/libp2p/go-libp2p-kad-dht v0.2.1/go.mod h1:k7ONOlup7HKzQ68dE6lSnp07cdxdkmnRa+6B4Fh9/w0=
github.com/libp2p/go-libp2p-kbucket v0.2.1 h1:q9Jfwww9XnXc1K9dyYuARJxJvIvhgYVaQCuziO/dF3c=
github.com/libp2p/go-libp2p-kbucket v0.2.1/go.mod h1:/Rtu8tqbJ4WQ2KTCOMJhggMukOLNLNPY1EtEWWLxUvc=
github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8=
github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90=
github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo=
github.com/libp2p/go-libp2p-mplex v0.2.1 h1:E1xaJBQnbSiTHGI1gaBKmKhu1TUKkErKJnE8iGvirYI=
github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE=
github.com/libp2p/go-libp2p-nat v0.0.4 h1:+KXK324yaY701On8a0aGjTnw8467kW3ExKcqW2wwmyw=
github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY=
github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY=
github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY=
github.com/libp2p/go-libp2p-peerstore v0.1.3 h1:wMgajt1uM2tMiqf4M+4qWKVyyFc8SfA+84VV9glZq1M=
github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI=
github.com/libp2p/go-libp2p-record v0.1.1 h1:ZJK2bHXYUBqObHX+rHLSNrM3M8fmJUlUHrodDPPATmY=
github.com/libp2p/go-libp2p-record v0.1.1/go.mod h1:VRgKajOyMVgP/F0L5g3kH7SVskp17vFi2xheb5uMJtg=
github.com/libp2p/go-libp2p-routing v0.1.0 h1:hFnj3WR3E2tOcKaGpyzfP4gvFZ3t8JkQmbapN0Ct+oU=
github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE=
github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8=
github.com/libp2p/go-libp2p-secio v0.2.0 h1:ywzZBsWEEz2KNTn5RtzauEDq5RFEefPsttXYwAWqHng=
github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g=
github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4=
github.com/libp2p/go-libp2p-swarm v0.2.1/go.mod h1:x07b4zkMFo2EvgPV2bMTlNmdQc8i+74Jjio7xGvsTgU=
github.com/libp2p/go-libp2p-swarm v0.2.2 h1:T4hUpgEs2r371PweU3DuH7EOmBIdTBCwWs+FLcgx3bQ=
github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU=
github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
github.com/libp2p/go-libp2p-transport-upgrader v0.1.1 h1:PZMS9lhjK9VytzMCW3tWHAXtKXmlURSc3ZdvwEcKCzw=
github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA=
github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8=
github.com/libp2p/go-libp2p-yamux v0.2.1 h1:Q3XYNiKCC2vIxrvUJL+Jg1kiyeEaIDNKLjgEjo3VQdI=
github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI=
github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q=
github.com/libp2p/go-maddr-filter v0.0.5 h1:CW3AgbMO6vUvT4kf87y4N+0P8KUl2aqLYhrGyDUbLSg=
github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0=
github.com/libp2p/go-mplex v0.1.0 h1:/nBTy5+1yRyY82YaO6HXQRnO5IAGsXTjEJaR3LdTPc0=
github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=
github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA=
github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
github.com/libp2p/go-nat v0.0.3 h1:l6fKV+p0Xa354EqQOQP+d8CivdLM4kl5GxC1hSc/UeI=
github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI=
github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=
github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw=
github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4=
github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs=
github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14=
github.com/libp2p/go-stream-muxer-multistream v0.2.0 h1:714bRJ4Zy9mdhyTLJ+ZKiROmAFwUHpeRidG+q7LTQOg=
github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc=
github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc=
github.com/libp2p/go-tcp-transport v0.1.1 h1:yGlqURmqgNA2fvzjSgZNlHcsd/IulAnKM8Ncu+vlqnw=
github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY=
github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo=
github.com/libp2p/go-ws-transport v0.1.2 h1:VnxQcLfSGtqupqPpBNu8fUiCv+IN1RJ2BcVqQEM+z8E=
github.com/libp2p/go-ws-transport v0.1.2/go.mod h1:dsh2Ld8F+XNmzpkaAijmg5Is+e9l6/1tK/6VFOdN69Y=
github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/libp2p/go-yamux v1.2.3 h1:xX8A36vpXb59frIzWFdEgptLMsOANMFq2K7fPRlunYI=
github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.1.12 h1:WMhc1ik4LNkTg8U9l3hI1LvxKmIL+f1+WV/SZtCbDDA=
github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ= github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
github.com/multiformats/go-multiaddr v0.1.1 h1:rVAztJYMhCQ7vEFr8FvxW3mS+HF2eY/oPbOMeS0ZDnE=
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.1.0 h1:gsPeMvo91XvcsNlQXgJgfjYjbsVV99bvveguUvDBpyQ=
github.com/multiformats/go-multiaddr-dns v0.1.0/go.mod h1:01k2RAqtoXIuPa3DCavAE9/6jc6nM0H3EgZyfUhN2oY=
github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q=
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU=
github.com/multiformats/go-multiaddr-net v0.1.0 h1:ZepO8Ezwovd+7b5XPPDhQhayk1yt0AJpzQBpq9fejx4=
github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multicodec v0.1.6 h1:4u6lcjbE4VVVoigU4QJSSVYsGVP4j2jtDkR8lPwOrLE=
github.com/multiformats/go-multicodec v0.1.6/go.mod h1:lliaRHbcG8q33yf4Ot9BGD7JqR/Za9HE7HTyVyKwrUQ=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg=
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-multistream v0.1.0 h1:UpO6jrsjqs46mqAK3n6wKRYFhugss9ArzbyUzU+4wkQ=
github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 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.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
...@@ -160,11 +396,24 @@ github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d h1:1VUlQbCfkoSGv ...@@ -160,11 +396,24 @@ github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d h1:1VUlQbCfkoSGv
github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY= github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
...@@ -175,25 +424,58 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 ...@@ -175,25 +424,58 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/syndtr/goleveldb v0.0.0-20181105012736-f9080354173f h1:EEVjSRihF8NIbfyCcErpSpNHEKrY3s8EAwqiPENZZn8= github.com/syndtr/goleveldb v0.0.0-20181105012736-f9080354173f h1:EEVjSRihF8NIbfyCcErpSpNHEKrY3s8EAwqiPENZZn8=
github.com/syndtr/goleveldb v0.0.0-20181105012736-f9080354173f/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v0.0.0-20181105012736-f9080354173f/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw= github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw=
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.5.0 h1:dhq+O9pmNZFF6qAXpasMO1xSm7dL4qEz2ylfZN8BG9w= github.com/valyala/fasthttp v1.5.0 h1:dhq+O9pmNZFF6qAXpasMO1xSm7dL4qEz2ylfZN8BG9w=
github.com/valyala/fasthttp v1.5.0/go.mod h1:eriCz9OhZjKCGfJ185a/IDgNl0bg9IbzfpcslMZXU1c= github.com/valyala/fasthttp v1.5.0/go.mod h1:eriCz9OhZjKCGfJ185a/IDgNl0bg9IbzfpcslMZXU1c=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboadS0DvysUuJXZ4lWVv5Bh5i7+tbIyi+ck4=
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc h1:9lDbC6Rz4bwmou+oE6Dt4Cb2BGMur5eR/GYptkKUVHo=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f h1:M/lL30eFZTKnomXY6huvM6G0+gVquFNf6mxghaWlFUg=
github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8=
github.com/whyrusleeping/mafmt v1.2.8 h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA=
github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA=
github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 h1:Y1/FEOpaCpD21WxrmfeIYCFPuVPRCY2XZTWzTNHGw30=
github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds=
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50=
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4= go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4=
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
...@@ -205,8 +487,10 @@ golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73r ...@@ -205,8 +487,10 @@ golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
...@@ -217,16 +501,24 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG ...@@ -217,16 +501,24 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
...@@ -234,19 +526,28 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= ...@@ -234,19 +526,28 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a h1:lRlI5zu6AFy3iU/F8YWyNrAmn/tPCnhiTxfwhWb76eU= google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a h1:lRlI5zu6AFy3iU/F8YWyNrAmn/tPCnhiTxfwhWb76eU=
google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200310143817-43be25429f5a/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM= google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
...@@ -265,6 +566,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 h1:AFxeG48hT ...@@ -265,6 +566,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 h1:AFxeG48hT
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs= gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
......
...@@ -5,5 +5,6 @@ import ( ...@@ -5,5 +5,6 @@ import (
_ "github.com/33cn/plugin/plugin/crypto/init" //crypto init _ "github.com/33cn/plugin/plugin/crypto/init" //crypto init
_ "github.com/33cn/plugin/plugin/dapp/init" //dapp init _ "github.com/33cn/plugin/plugin/dapp/init" //dapp init
_ "github.com/33cn/plugin/plugin/mempool/init" //mempool init _ "github.com/33cn/plugin/plugin/mempool/init" //mempool init
_ "github.com/33cn/plugin/plugin/p2p/init" //p2p init
_ "github.com/33cn/plugin/plugin/store/init" //store init _ "github.com/33cn/plugin/plugin/store/init" //store init
) )
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"sync"
"time"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
)
// Start addrbook start
func (a *AddrBook) Start() error {
log.Debug("addrbook start")
a.loadDb()
go a.saveRoutine()
return nil
}
// Close addrbook close
func (a *AddrBook) Close() {
a.Quit <- struct{}{}
a.bookDb.Close()
}
// AddrBook peer address manager
type AddrBook struct {
mtx sync.Mutex
ourAddrs map[string]*NetAddress
addrPeer map[string]*KnownAddress
p2pCfg *types.P2P
cfg *subConfig
keymtx sync.Mutex
privkey string
pubkey string
bookDb db.DB
Quit chan struct{}
}
// KnownAddress defines known address type
type KnownAddress struct {
kmtx sync.Mutex
Addr *NetAddress `json:"addr"`
Attempts uint `json:"attempts"`
LastAttempt time.Time `json:"lastattempt"`
LastSuccess time.Time `json:"lastsuccess"`
}
// GetPeerStat get peer stat
func (a *AddrBook) GetPeerStat(addr string) *KnownAddress {
a.mtx.Lock()
defer a.mtx.Unlock()
if peer, ok := a.addrPeer[addr]; ok {
return peer
}
return nil
}
func (a *AddrBook) setAddrStat(addr string, run bool) (*KnownAddress, bool) {
a.mtx.Lock()
defer a.mtx.Unlock()
if peer, ok := a.addrPeer[addr]; ok {
if run {
peer.markGood()
} else {
peer.markAttempt()
}
return peer, true
}
return nil, false
}
// NewAddrBook create a addrbook
func NewAddrBook(cfg *types.P2P, subCfg *subConfig) *AddrBook {
a := &AddrBook{
ourAddrs: make(map[string]*NetAddress),
addrPeer: make(map[string]*KnownAddress),
p2pCfg: cfg,
Quit: make(chan struct{}, 1),
cfg: subCfg,
}
err := a.Start()
if err != nil {
return nil
}
return a
}
func newKnownAddress(addr *NetAddress) *KnownAddress {
return &KnownAddress{
kmtx: sync.Mutex{},
Addr: addr,
Attempts: 0,
LastAttempt: types.Now(),
}
}
func (ka *KnownAddress) markGood() {
ka.kmtx.Lock()
defer ka.kmtx.Unlock()
now := types.Now()
ka.LastAttempt = now
ka.Attempts = 0
ka.LastSuccess = now
}
// Copy a KnownAddress
func (ka *KnownAddress) Copy() *KnownAddress {
ka.kmtx.Lock()
ret := KnownAddress{
Addr: ka.Addr.Copy(),
Attempts: ka.Attempts,
LastAttempt: ka.LastAttempt,
LastSuccess: ka.LastSuccess,
}
ka.kmtx.Unlock()
return &ret
}
func (ka *KnownAddress) markAttempt() {
ka.kmtx.Lock()
defer ka.kmtx.Unlock()
now := types.Now()
ka.LastAttempt = now
ka.Attempts++
}
// GetAttempts return attempts
func (ka *KnownAddress) GetAttempts() uint {
ka.kmtx.Lock()
defer ka.kmtx.Unlock()
return ka.Attempts
}
// ISOurAddress determine if the address is ours
func (a *AddrBook) ISOurAddress(addr *NetAddress) bool {
a.mtx.Lock()
defer a.mtx.Unlock()
if _, ok := a.ourAddrs[addr.String()]; ok {
return true
}
return false
}
// IsOurStringAddress determine if the address is ours
func (a *AddrBook) IsOurStringAddress(addr string) bool {
a.mtx.Lock()
defer a.mtx.Unlock()
if _, ok := a.ourAddrs[addr]; ok {
return true
}
return false
}
// AddOurAddress add a address for ours
func (a *AddrBook) AddOurAddress(addr *NetAddress) {
a.mtx.Lock()
defer a.mtx.Unlock()
log.Debug("Add our address to book", "addr", addr)
a.ourAddrs[addr.String()] = addr
}
// Size return addrpeer size
func (a *AddrBook) Size() int {
a.mtx.Lock()
defer a.mtx.Unlock()
return len(a.addrPeer)
}
type addrBookJSON struct {
Addrs []*KnownAddress `json:"addrs"`
}
func (a *AddrBook) saveToDb() {
a.mtx.Lock()
defer a.mtx.Unlock()
addrs := []*KnownAddress{}
seeds := a.cfg.Seeds
seedsMap := make(map[string]int)
for index, seed := range seeds {
seedsMap[seed] = index
}
for _, ka := range a.addrPeer {
if _, ok := seedsMap[ka.Addr.String()]; !ok {
addrs = append(addrs, ka.Copy())
}
}
if len(addrs) == 0 {
return
}
aJSON := &addrBookJSON{
Addrs: addrs,
}
jsonBytes, err := json.Marshal(aJSON)
if err != nil {
log.Error("Failed to save AddrBook to file", "err", err)
return
}
log.Debug("saveToDb", "addrs", string(jsonBytes))
err = a.bookDb.Set([]byte(addrkeyTag), jsonBytes)
if err != nil {
panic(err)
}
}
func (a *AddrBook) genPubkey(privkey string) string {
maxRetry := 10
for i := 0; i < maxRetry; i++ {
pubkey, err := P2pComm.Pubkey(privkey)
if err == nil {
return pubkey
}
}
panic(fmt.Sprintf("get pubkey from privkey:%s failed", privkey))
}
// Returns false if file does not exist.
// cmn.Panics if file is corrupt.
func (a *AddrBook) loadDb() bool {
dbPath := a.p2pCfg.DbPath + "/" + P2PTypeName
a.bookDb = db.NewDB("addrbook", a.p2pCfg.Driver, dbPath, a.p2pCfg.DbCache)
privkey, err := a.bookDb.Get([]byte(privKeyTag))
if len(privkey) != 0 && err == nil {
a.setKey(string(privkey), a.genPubkey(string(privkey)))
}
iteror := a.bookDb.Iterator(nil, nil, false)
for iteror.Next() {
if string(iteror.Key()) == addrkeyTag {
//读取存入的其他节点地址信息
aJSON := &addrBookJSON{}
dec := json.NewDecoder(strings.NewReader(string(iteror.Value())))
err := dec.Decode(aJSON)
if err != nil {
log.Crit("Error reading file %s: %v", a.p2pCfg.DbPath, err)
}
for _, ka := range aJSON.Addrs {
log.Debug("AddrBookloadDb", "peer", ka.Addr.String())
netaddr, err := NewNetAddressString(ka.Addr.String())
if err != nil {
continue
}
a.AddAddress(netaddr, ka)
}
}
}
return true
}
// Save saves the book.
func (a *AddrBook) Save() {
a.saveToDb()
}
func (a *AddrBook) saveRoutine() {
dumpAddressTicker := time.NewTicker(120 * time.Second)
defer dumpAddressTicker.Stop()
out:
for {
select {
case <-dumpAddressTicker.C:
a.Save()
case <-a.Quit:
break out
}
}
log.Info("Address handler done")
}
// AddAddress add a address for ours
// NOTE: addr must not be nil
func (a *AddrBook) AddAddress(addr *NetAddress, ka *KnownAddress) {
a.mtx.Lock()
defer a.mtx.Unlock()
log.Debug("Add address to book", "addr", addr)
if addr == nil {
return
}
if _, ok := a.ourAddrs[addr.String()]; ok {
// Ignore our own listener address.
return
}
//已经添加的不重复添加
if _, ok := a.addrPeer[addr.String()]; ok {
return
}
if nil == ka {
ka = newKnownAddress(addr)
}
a.addrPeer[ka.Addr.String()] = ka
}
// RemoveAddr remove address
func (a *AddrBook) RemoveAddr(peeraddr string) {
a.mtx.Lock()
defer a.mtx.Unlock()
if _, ok := a.addrPeer[peeraddr]; ok {
delete(a.addrPeer, peeraddr)
}
}
// GetPeers return peerlist
func (a *AddrBook) GetPeers() []*NetAddress {
a.mtx.Lock()
defer a.mtx.Unlock()
var peerlist []*NetAddress
for _, peer := range a.addrPeer {
peerlist = append(peerlist, peer.Addr)
}
return peerlist
}
// GetAddrs return addrlist
func (a *AddrBook) GetAddrs() []string {
a.mtx.Lock()
defer a.mtx.Unlock()
var addrlist []string
for _, peer := range a.addrPeer {
if peer.GetAttempts() == 0 {
addrlist = append(addrlist, peer.Addr.String())
}
}
return addrlist
}
func (a *AddrBook) initKey() {
maxRetry := 10
for i := 0; i < maxRetry; i++ {
priv, pub, err := P2pComm.GenPrivPubkey()
if err == nil {
a.setKey(hex.EncodeToString(priv), hex.EncodeToString(pub))
return
}
}
panic(fmt.Sprintf("p2p initPrivPubkey failed"))
}
func (a *AddrBook) setKey(privkey, pubkey string) {
a.keymtx.Lock()
defer a.keymtx.Unlock()
a.privkey = privkey
a.pubkey = pubkey
}
//ResetPeerkey reset priv,pub key
func (a *AddrBook) ResetPeerkey(privkey, pubkey string) {
if privkey == "" || pubkey == "" {
a.initKey()
privkey, pubkey = a.GetPrivPubKey()
}
a.setKey(privkey, pubkey)
err := a.bookDb.Set([]byte(privKeyTag), []byte(privkey))
if err != nil {
panic(err)
}
}
// GetPrivPubKey return privkey and pubkey
func (a *AddrBook) GetPrivPubKey() (string, string) {
a.keymtx.Lock()
defer a.keymtx.Unlock()
return a.privkey, a.pubkey
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"bytes"
"encoding/binary"
"encoding/hex"
"math/rand"
"net"
"strings"
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/types"
"google.golang.org/grpc"
)
// P2pComm p2p communication
var P2pComm Comm
// Comm information
type Comm struct{}
// AddrRouteble address router ,return enbale address
func (Comm) AddrRouteble(addrs []string, version int32) []string {
var enableAddrs []string
for _, addr := range addrs {
netaddr, err := NewNetAddressString(addr)
if err != nil {
log.Error("AddrRouteble", "NewNetAddressString", err.Error())
continue
}
conn, err := netaddr.DialTimeout(version)
if err != nil {
//log.Error("AddrRouteble", "DialTimeout", err.Error())
continue
}
err = conn.Close()
if err != nil {
log.Error("AddrRouteble", "conn.Close err", err.Error())
}
enableAddrs = append(enableAddrs, addr)
}
return enableAddrs
}
// RandStr return a rand string
func (c Comm) RandStr(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
r := rand.New(rand.NewSource(types.Now().Unix()))
b := make([]rune, n)
for i := range b {
b[i] = letters[r.Intn(len(letters))]
}
return string(b)
}
// GetLocalAddr get local address ,return address
func (c Comm) GetLocalAddr() string {
conn, err := net.Dial("udp", "114.114.114.114:80")
if err != nil {
log.Error(err.Error())
return ""
}
defer conn.Close()
log.Debug(strings.Split(conn.LocalAddr().String(), ":")[0])
return strings.Split(conn.LocalAddr().String(), ":")[0]
}
func (c Comm) dialPeerWithAddress(addr *NetAddress, persistent bool, node *Node) (*Peer, error) {
log.Debug("dialPeerWithAddress")
conn, err := addr.DialTimeout(node.nodeInfo.channelVersion)
if err != nil {
return nil, err
}
peer, err := c.newPeerFromConn(conn, addr, node)
if err != nil {
err = conn.Close()
return nil, err
}
peer.SetAddr(addr)
log.Debug("dialPeerWithAddress", "peer", peer.Addr(), "persistent:", persistent)
if persistent {
peer.MakePersistent()
}
return peer, nil
}
func (c Comm) newPeerFromConn(rawConn *grpc.ClientConn, remote *NetAddress, node *Node) (*Peer, error) {
// Key and NodeInfo are set after Handshake
p := NewPeer(rawConn, node, remote)
return p, nil
}
func (c Comm) dialPeer(addr *NetAddress, node *Node) (*Peer, error) {
log.Debug("dialPeer", "will connect", addr.String())
var persistent bool
if _, ok := node.cfgSeeds.Load(addr.String()); ok {
persistent = true
}
peer, err := c.dialPeerWithAddress(addr, persistent, node)
if err != nil {
log.Error("dialPeer", "nodeListenAddr", node.nodeInfo.listenAddr.str, "peerAddr", addr.str, "err", err)
return nil, err
}
//获取远程节点的信息 peer
log.Debug("dialPeer", "peer", peer)
return peer, nil
}
// GenPrivPubkey return key and pubkey in bytes
func (c Comm) GenPrivPubkey() ([]byte, []byte, error) {
cr, err := crypto.New(types.GetSignName("", types.SECP256K1))
if err != nil {
log.Error("CryPto Error", "Error", err.Error())
return nil, nil, err
}
key, err := cr.GenKey()
if err != nil {
log.Error("GenKey", "Error", err)
return nil, nil, err
}
return key.Bytes(), key.PubKey().Bytes(), nil
}
// Pubkey get pubkey by priv key
func (c Comm) Pubkey(key string) (string, error) {
cr, err := crypto.New(types.GetSignName("", types.SECP256K1))
if err != nil {
log.Error("CryPto Error", "Error", err.Error())
return "", err
}
pribyts, err := hex.DecodeString(key)
if err != nil {
log.Error("DecodeString Error", "Error", err.Error())
return "", err
}
priv, err := cr.PrivKeyFromBytes(pribyts)
if err != nil {
log.Error("Load PrivKey", "Error", err.Error())
return "", err
}
return hex.EncodeToString(priv.PubKey().Bytes()), nil
}
// NewPingData get ping node ,return p2pping
func (c Comm) NewPingData(nodeInfo *NodeInfo) (*types.P2PPing, error) {
randNonce := rand.New(rand.NewSource(time.Now().UnixNano())).Int63()
ping := &types.P2PPing{Nonce: randNonce, Addr: nodeInfo.GetExternalAddr().IP.String(), Port: int32(nodeInfo.GetExternalAddr().Port)}
var err error
p2pPrivKey, _ := nodeInfo.addrBook.GetPrivPubKey()
ping, err = c.Signature(p2pPrivKey, ping)
if err != nil {
log.Error("Signature", "Error", err.Error())
return nil, err
}
return ping, nil
}
// Signature nodedata by key
func (c Comm) Signature(key string, in *types.P2PPing) (*types.P2PPing, error) {
data := types.Encode(in)
cr, err := crypto.New(types.GetSignName("", types.SECP256K1))
if err != nil {
log.Error("CryPto Error", "Error", err.Error())
return nil, err
}
pribyts, err := hex.DecodeString(key)
if err != nil {
log.Error("DecodeString Error", "Error", err.Error())
return nil, err
}
priv, err := cr.PrivKeyFromBytes(pribyts)
if err != nil {
log.Error("Load PrivKey", "Error", err.Error())
return nil, err
}
in.Sign = new(types.Signature)
in.Sign.Signature = priv.Sign(data).Bytes()
in.Sign.Ty = types.SECP256K1
in.Sign.Pubkey = priv.PubKey().Bytes()
return in, nil
}
// CheckSign check signature data
func (c Comm) CheckSign(in *types.P2PPing) bool {
sign := in.GetSign()
if sign == nil {
log.Error("CheckSign Get sign err")
return false
}
cr, err := crypto.New(types.GetSignName("", int(sign.Ty)))
if err != nil {
log.Error("CheckSign", "crypto.New err", err.Error())
return false
}
pub, err := cr.PubKeyFromBytes(sign.Pubkey)
if err != nil {
log.Error("CheckSign", "PubKeyFromBytes err", err.Error())
return false
}
signbytes, err := cr.SignatureFromBytes(sign.Signature)
if err != nil {
log.Error("CheckSign", "SignatureFromBytes err", err.Error())
return false
}
in.Sign = nil
data := types.Encode(in)
if pub.VerifyBytes(data, signbytes) {
in.Sign = sign
return true
}
return false
}
// CollectPeerStat collect peer stat and report
func (c Comm) CollectPeerStat(err error, peer *Peer) {
if err != nil {
if err == types.ErrVersion || err == types.ErrP2PChannel {
peer.version.SetSupport(false)
}
peer.peerStat.NotOk()
} else {
peer.peerStat.Ok()
}
c.reportPeerStat(peer)
}
func (c Comm) reportPeerStat(peer *Peer) {
timeout := time.NewTimer(time.Second)
select {
case peer.node.nodeInfo.monitorChan <- peer:
case <-timeout.C:
timeout.Stop()
return
}
if !timeout.Stop() {
<-timeout.C
}
}
// BytesToInt32 bytes to int32 type
func (c Comm) BytesToInt32(b []byte) int32 {
bytesBuffer := bytes.NewBuffer(b)
var tmp int32
err := binary.Read(bytesBuffer, binary.LittleEndian, &tmp)
if err != nil {
log.Error("BytesToInt32", "binary.Read err", err.Error())
return tmp
}
return tmp
}
// Int32ToBytes int32 to bytes type
func (c Comm) Int32ToBytes(n int32) []byte {
tmp := n
bytesBuffer := bytes.NewBuffer([]byte{})
err := binary.Write(bytesBuffer, binary.LittleEndian, tmp)
if err != nil {
return nil
}
return bytesBuffer.Bytes()
}
// GrpcConfig grpc config
func (c Comm) GrpcConfig() grpc.ServiceConfig {
var defaulttimeout = 20 * time.Second
var MethodConf = map[string]grpc.MethodConfig{
"/types.p2pgservice/Ping": {Timeout: &defaulttimeout},
"/types.p2pgservice/Version2": {Timeout: &defaulttimeout},
"/types.p2pgservice/BroadCastTx": {Timeout: &defaulttimeout},
"/types.p2pgservice/GetMemPool": {Timeout: &defaulttimeout},
"/types.p2pgservice/GetBlocks": {Timeout: &defaulttimeout},
"/types.p2pgservice/GetPeerInfo": {Timeout: &defaulttimeout},
"/types.p2pgservice/BroadCastBlock": {Timeout: &defaulttimeout},
"/types.p2pgservice/GetAddr": {Timeout: &defaulttimeout},
"/types.p2pgservice/GetHeaders": {Timeout: &defaulttimeout},
"/types.p2pgservice/RemotePeerAddr": {Timeout: &defaulttimeout},
"/types.p2pgservice/RemotePeerNatOk": {Timeout: &defaulttimeout},
}
return grpc.ServiceConfig{Methods: MethodConf}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
pb "github.com/33cn/chain33/types"
"google.golang.org/grpc"
)
// MConnection contains node, grpc client, p2pgserviceClient, netaddress, peer
type MConnection struct {
node *Node
gconn *grpc.ClientConn
gcli pb.P2PgserviceClient // source connection
remoteAddress *NetAddress //peer 的地址
peer *Peer
}
// NewMConnection wraps net.Conn and creates multiplex connection
func NewMConnection(conn *grpc.ClientConn, remote *NetAddress, peer *Peer) *MConnection {
log.Info("NewMConnection p2p client", "addr", remote)
mconn := &MConnection{
gconn: conn,
gcli: pb.NewP2PgserviceClient(conn),
peer: peer,
}
mconn.node = peer.node
mconn.remoteAddress = remote
return mconn
}
// Close mconnection
func (c *MConnection) Close() {
err := c.gconn.Close()
if err != nil {
log.Error("Mconnection", "Close err", err)
}
log.Debug("Mconnection", "Close", "^_^!")
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"time"
)
// time limit for timeout
var (
UpdateState = 2 * time.Second
PingTimeout = 14 * time.Second
DefaultSendTimeout = 10 * time.Second
DialTimeout = 5 * time.Second
mapUpdateInterval = 45 * time.Hour
StreamPingTimeout = 20 * time.Second
MonitorPeerInfoInterval = 10 * time.Second
MonitorPeerNumInterval = 30 * time.Second
MonitorReBalanceInterval = 15 * time.Minute
GetAddrFromAddrBookInterval = 5 * time.Second
GetAddrFromOnlineInterval = 5 * time.Second
GetAddrFromGitHubInterval = 5 * time.Minute
CheckActivePeersInterVal = 5 * time.Second
CheckBlackListInterVal = 30 * time.Second
CheckCfgSeedsInterVal = 1 * time.Minute
)
const (
msgTx = 1
msgBlock = 2
tryMapPortTimes = 20
maxSamIPNum = 20
)
const (
//defalutNatPort = 23802
maxOutBoundNum = 25
stableBoundNum = 15
maxAttemps = 5
protocol = "tcp"
externalPortTag = "externalport"
)
const (
nodeNetwork = 1
nodeGetUTXO = 2
nodeBloom = 4
)
const (
// Service service number
Service int32 = nodeBloom + nodeNetwork + nodeGetUTXO
)
// leveldb 中p2p privkey,addrkey
const (
addrkeyTag = "addrs"
privKeyTag = "privkey"
)
//TTL
const (
DefaultLtTxBroadCastTTL = 3
DefaultMaxTxBroadCastTTL = 25
DefaultMinLtBlockTxNum = 5
)
// P2pCacheTxSize p2pcache size of transaction
const (
PeerAddrCacheNum = 1000
//接收的交易哈希过滤缓存设为mempool最大接收交易量
TxRecvFilterCacheNum = 10240
BlockFilterCacheNum = 50
//发送过滤主要用于发送时冗余检测, 发送完即可以被删除, 维护较小缓存数
TxSendFilterCacheNum = 500
BlockCacheNum = 10
MaxBlockCacheByteSize = 100 * 1024 * 1024
)
// TestNetSeeds test seeds of net
var TestNetSeeds = []string{
"47.97.223.101:13802",
}
// MainNetSeeds built-in list of seed
var MainNetSeeds = []string{
"116.62.14.25:13802",
"114.55.95.234:13802",
"115.28.184.14:13802",
"39.106.166.159:13802",
"39.106.193.172:13802",
"47.106.114.93:13802",
"120.76.100.165:13802",
"120.24.85.66:13802",
"120.24.92.123:13802",
"161.117.7.127:13802",
"161.117.9.54:13802",
"161.117.5.95:13802",
"161.117.7.28:13802",
"161.117.8.242:13802",
"161.117.6.193:13802",
"161.117.8.158:13802",
"47.88.157.209:13802",
"47.74.215.41:13802",
"47.74.128.69:13802",
"47.74.178.226:13802",
"47.88.154.76:13802",
"47.74.151.226:13802",
"47.245.31.41:13802",
"47.245.57.239:13802",
"47.245.54.118:13802",
"47.245.54.121:13802",
"47.245.56.140:13802",
"47.245.52.211:13802",
"47.91.88.195:13802",
"47.91.72.71:13802",
"47.91.91.38:13802",
"47.91.94.224:13802",
"47.91.75.191:13802",
"47.254.152.172:13802",
"47.252.0.181:13802",
"47.90.246.246:13802",
"47.90.208.100:13802",
"47.89.182.70:13802",
"47.90.207.173:13802",
"47.89.188.54:13802",
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
//p2p 网络模块:
//主要的功能是实现区块链数据的广播,交易的广播的功能。
//p2p 模块的接口:
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"fmt"
"io"
"sort"
"sync"
"sync/atomic"
"time"
pb "github.com/33cn/chain33/types"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// Invs datastruct
type Invs []*pb.Inventory
//Len size of the Invs data
func (i Invs) Len() int {
return len(i)
}
//Less Sort from low to high
func (i Invs) Less(a, b int) bool {
return i[a].GetHeight() < i[b].GetHeight()
}
//Swap the param
func (i Invs) Swap(a, b int) {
i[a], i[b] = i[b], i[a]
}
// DownloadJob defines download job type
type DownloadJob struct {
wg sync.WaitGroup
p2pcli *Cli
canceljob int32
mtx sync.Mutex
busyPeer map[string]*peerJob
downloadPeers []*Peer
MaxJob int32
retryItems Invs
}
type peerJob struct {
limit int32
}
// NewDownloadJob create a downloadjob object
func NewDownloadJob(p2pcli *Cli, peers []*Peer) *DownloadJob {
job := new(DownloadJob)
job.p2pcli = p2pcli
job.busyPeer = make(map[string]*peerJob)
job.downloadPeers = peers
job.retryItems = make([]*pb.Inventory, 0)
job.MaxJob = 5
if len(peers) < 5 {
job.MaxJob = 10
}
//job.okChan = make(chan *pb.Inventory, 512)
return job
}
func (d *DownloadJob) getDownloadPeers() []*Peer {
d.mtx.Lock()
defer d.mtx.Unlock()
peers := make([]*Peer, len(d.downloadPeers))
copy(peers, d.downloadPeers)
return peers
}
func (d *DownloadJob) isBusyPeer(pid string) bool {
d.mtx.Lock()
defer d.mtx.Unlock()
if pjob, ok := d.busyPeer[pid]; ok {
return atomic.LoadInt32(&pjob.limit) >= d.MaxJob //每个节点最多同时接受10个下载任务
}
return false
}
func (d *DownloadJob) getJobNum(pid string) int32 {
d.mtx.Lock()
defer d.mtx.Unlock()
if pjob, ok := d.busyPeer[pid]; ok {
return atomic.LoadInt32(&pjob.limit)
}
return 0
}
func (d *DownloadJob) setBusyPeer(pid string) {
d.mtx.Lock()
defer d.mtx.Unlock()
if pjob, ok := d.busyPeer[pid]; ok {
atomic.AddInt32(&pjob.limit, 1)
d.busyPeer[pid] = pjob
return
}
d.busyPeer[pid] = &peerJob{1}
}
func (d *DownloadJob) removePeer(pid string) {
d.mtx.Lock()
defer d.mtx.Unlock()
for i, pr := range d.downloadPeers {
if pr.GetPeerName() == pid {
if i != len(d.downloadPeers)-1 {
d.downloadPeers = append(d.downloadPeers[:i], d.downloadPeers[i+1:]...)
return
}
d.downloadPeers = d.downloadPeers[:i]
return
}
}
}
// ResetDownloadPeers reset download peers
func (d *DownloadJob) ResetDownloadPeers(peers []*Peer) {
d.mtx.Lock()
defer d.mtx.Unlock()
copy(d.downloadPeers, peers)
}
func (d *DownloadJob) avalidPeersNum() int {
d.mtx.Lock()
defer d.mtx.Unlock()
return len(d.downloadPeers)
}
func (d *DownloadJob) setFreePeer(pid string) {
d.mtx.Lock()
defer d.mtx.Unlock()
if pjob, ok := d.busyPeer[pid]; ok {
if atomic.AddInt32(&pjob.limit, -1) <= 0 {
delete(d.busyPeer, pid)
return
}
d.busyPeer[pid] = pjob
}
}
//加入到重试数组
func (d *DownloadJob) appendRetryItem(item *pb.Inventory) {
d.mtx.Lock()
defer d.mtx.Unlock()
d.retryItems = append(d.retryItems, item)
}
// GetFreePeer get free peer ,return peer
func (d *DownloadJob) GetFreePeer(blockHeight int64) *Peer {
infos := d.p2pcli.network.node.nodeInfo.peerInfos.GetPeerInfos()
var minJobNum int32 = 10
var bestPeer *Peer
//对download peer读取需要增加保护
for _, peer := range d.getDownloadPeers() {
peerName := peer.GetPeerName()
if d.isBusyPeer(peerName) {
continue
}
if jobNum := d.getJobNum(peerName); jobNum < minJobNum &&
infos[peerName].GetHeader().GetHeight() >= blockHeight {
minJobNum = jobNum
bestPeer = peer
}
}
return bestPeer
}
// CancelJob cancel the downloadjob object
func (d *DownloadJob) CancelJob() {
atomic.StoreInt32(&d.canceljob, 1)
}
func (d *DownloadJob) isCancel() bool {
return atomic.LoadInt32(&d.canceljob) == 1
}
// DownloadBlock download the block
func (d *DownloadJob) DownloadBlock(invs []*pb.Inventory,
bchan chan *pb.BlockPid) []*pb.Inventory {
if d.isCancel() {
return nil
}
for _, inv := range invs { //让一个节点一次下载一个区块,下载失败区块,交给下一轮下载
//获取当前任务数最少的节点,相当于 下载速度最快的节点
freePeer := d.GetFreePeer(inv.GetHeight())
for freePeer == nil {
log.Debug("no free peer")
time.Sleep(time.Millisecond * 100)
freePeer = d.GetFreePeer(inv.GetHeight())
}
d.setBusyPeer(freePeer.GetPeerName())
d.wg.Add(1)
go func(peer *Peer, inv *pb.Inventory) {
defer d.wg.Done()
err := d.syncDownloadBlock(peer, inv, bchan)
if err != nil {
d.removePeer(peer.GetPeerName())
log.Error("DownloadBlock:syncDownloadBlock", "height", inv.GetHeight(), "peer", peer.GetPeerName(), "err", err)
d.appendRetryItem(inv) //失败的下载,放在下一轮ReDownload进行下载
} else {
d.setFreePeer(peer.GetPeerName())
}
}(freePeer, inv)
}
//等待下载任务
d.wg.Wait()
retryInvs := d.retryItems
//存在重试项
if retryInvs.Len() > 0 {
d.retryItems = make([]*pb.Inventory, 0)
sort.Sort(retryInvs)
}
return retryInvs
}
func (d *DownloadJob) syncDownloadBlock(peer *Peer, inv *pb.Inventory, bchan chan *pb.BlockPid) error {
//每次下载一个高度的数据,通过bchan返回上层
if peer == nil {
return fmt.Errorf("peer is not exist")
}
if !peer.GetRunning() {
return fmt.Errorf("peer not running")
}
var p2pdata pb.P2PGetData
p2pdata.Version = d.p2pcli.network.node.nodeInfo.channelVersion
p2pdata.Invs = []*pb.Inventory{inv}
ctx, cancel := context.WithCancel(context.Background())
//主动取消grpc流, 即时释放资源
defer cancel()
beg := pb.Now()
resp, err := peer.mconn.gcli.GetData(ctx, &p2pdata, grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
log.Error("syncDownloadBlock", "GetData err", err.Error())
return err
}
defer func() {
log.Debug("download", "frompeer", peer.Addr(), "blockheight", inv.GetHeight(), "downloadcost", pb.Since(beg))
}()
invData, err := resp.Recv()
if err != nil && err != io.EOF {
log.Error("syncDownloadBlock", "RecvData err", err.Error())
return err
}
//返回单个数据条目
if invData == nil || len(invData.Items) != 1 {
return fmt.Errorf("InvalidRecvData")
}
block := invData.Items[0].GetBlock()
log.Debug("download", "frompeer", peer.Addr(), "blockheight", inv.GetHeight(), "blockSize", block.Size())
bchan <- &pb.BlockPid{Pid: peer.GetPeerName(), Block: block} //加入到输出通道
return nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"fmt"
"math/rand"
"net"
"sync"
"time"
pb "github.com/33cn/chain33/types"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
pr "google.golang.org/grpc/peer"
"google.golang.org/grpc/stats"
)
// Listener the actions
type Listener interface {
Close1()
Start()
}
// Start server start
func (l *listener) Start() {
l.p2pserver.Start()
go l.server.Serve(l.netlistener)
}
// Close server close
func (l *listener) Close() {
err := l.netlistener.Close()
if err != nil {
log.Error("Close", "netlistener.Close() err", err)
}
go l.server.Stop()
l.p2pserver.Close()
log.Info("stop", "server", "close")
}
type listener struct {
server *grpc.Server
nodeInfo *NodeInfo
p2pserver *P2pserver
node *Node
netlistener net.Listener
}
// newListener produce a server object
func newListener(protocol string, node *Node) *listener {
Retry:
log.Info("newListener", "localPort", node.listenPort)
l, err := net.Listen(protocol, fmt.Sprintf(":%v", node.listenPort))
if err != nil {
log.Error("Failed to listen", "Error", err.Error())
for {
randPort := rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(65535)
if int(randPort) == node.listenPort || randPort < 2048 {
continue
}
node.listenPort = int(randPort)
break
}
log.Info("Flush Listen Port", "RandPort", node.listenPort)
goto Retry
}
dl := &listener{
nodeInfo: node.nodeInfo,
node: node,
netlistener: l,
}
pServer := NewP2pServer()
pServer.node = dl.node
//一元拦截器 接口调用之前进行校验拦截
interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
//checkAuth
getctx, ok := pr.FromContext(ctx)
if !ok {
return nil, fmt.Errorf("")
}
ip, _, err := net.SplitHostPort(getctx.Addr.String())
if err != nil {
return nil, err
}
if pServer.node.nodeInfo.blacklist.Has(ip) {
return nil, fmt.Errorf("blacklist %v no authorized", ip)
}
if !auth(ip) {
log.Error("interceptor", "auth faild", ip)
//把相应的IP地址加入黑名单中
pServer.node.nodeInfo.blacklist.Add(ip, int64(3600))
return nil, fmt.Errorf("auth faild %v no authorized", ip)
}
// Continue processing the request
return handler(ctx, req)
}
//流拦截器
interceptorStream := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
getctx, ok := pr.FromContext(ss.Context())
if !ok {
log.Error("interceptorStream", "FromContext error", "")
return fmt.Errorf("stream Context err")
}
ip, _, err := net.SplitHostPort(getctx.Addr.String())
if err != nil {
return err
}
if pServer.node.nodeInfo.blacklist.Has(ip) {
return fmt.Errorf("blacklist %v no authorized", ip)
}
if !auth(ip) {
log.Error("interceptorStream", "auth faild", ip)
//把相应的IP地址加入黑名单中
pServer.node.nodeInfo.blacklist.Add(ip, int64(3600))
return fmt.Errorf("auth faild %v no authorized", ip)
}
return handler(srv, ss)
}
var opts []grpc.ServerOption
opts = append(opts, grpc.UnaryInterceptor(interceptor), grpc.StreamInterceptor(interceptorStream))
maxMsgSize := pb.MaxBlockSize + 1024*1024 //最大传输数据 最大区块大小
msgRecvOp := grpc.MaxRecvMsgSize(maxMsgSize) //设置最大接收数据
msgSendOp := grpc.MaxSendMsgSize(maxMsgSize) //设置最大发送数据
kaep := keepalive.EnforcementPolicy{
MinTime: 10 * time.Second, //只允许不低于10s频率的ping周期
PermitWithoutStream: true,
}
var keepparm keepalive.ServerParameters
keepparm.Time = 5 * time.Minute
keepparm.Timeout = 50 * time.Second
maxStreams := grpc.MaxConcurrentStreams(1000)
keepOp := grpc.KeepaliveParams(keepparm)
StatsOp := grpc.StatsHandler(&statshandler{})
opts = append(opts, msgRecvOp, msgSendOp, grpc.KeepaliveEnforcementPolicy(kaep), keepOp, maxStreams, StatsOp)
dl.server = grpc.NewServer(opts...)
dl.p2pserver = pServer
pb.RegisterP2PgserviceServer(dl.server, pServer)
return dl
}
type statshandler struct{}
func (h *statshandler) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {
return context.WithValue(ctx, connCtxKey{}, info)
}
func (h *statshandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
return ctx
}
func (h *statshandler) HandleConn(ctx context.Context, s stats.ConnStats) {
if ctx == nil {
return
}
tag, ok := getConnTagFromContext(ctx)
if !ok {
log.Error("can not get conn tag")
return
}
ip, _, err := net.SplitHostPort(tag.RemoteAddr.String())
if err != nil {
return
}
connsMutex.Lock()
defer connsMutex.Unlock()
if _, ok := conns[ip]; !ok {
conns[ip] = 0
}
switch s.(type) {
case *stats.ConnBegin:
conns[ip] = conns[ip] + 1
case *stats.ConnEnd:
conns[ip] = conns[ip] - 1
if conns[ip] <= 0 {
delete(conns, ip)
}
log.Debug("ip connend", "ip", ip, "n", conns[ip])
default:
log.Error("illegal ConnStats type")
}
}
// HandleRPC 为空.
func (h *statshandler) HandleRPC(ctx context.Context, s stats.RPCStats) {}
type connCtxKey struct{}
var connsMutex sync.Mutex
var conns = make(map[string]uint)
func getConnTagFromContext(ctx context.Context) (*stats.ConnTagInfo, bool) {
tag, ok := ctx.Value(connCtxKey{}).(*stats.ConnTagInfo)
return tag, ok
}
func auth(checkIP string) bool {
connsMutex.Lock()
defer connsMutex.Unlock()
count, ok := conns[checkIP]
if ok && count > maxSamIPNum {
log.Error("AuthCheck", "sameIP num:", count, "checkIP:", checkIP, "diffIP num:", len(conns))
return false
}
return true
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"bytes"
"io"
"net/http"
"strings"
"time"
"github.com/33cn/chain33/p2p/utils"
"github.com/33cn/chain33/types"
)
var (
peerAddrFilter = utils.NewFilter(PeerAddrCacheNum)
)
func (n *Node) destroyPeer(peer *Peer) {
log.Debug("deleteErrPeer", "Delete peer", peer.Addr(), "running", peer.GetRunning(),
"version support", peer.version.IsSupport())
n.nodeInfo.addrBook.RemoveAddr(peer.Addr())
n.remove(peer.Addr())
}
func (n *Node) monitorErrPeer() {
for {
peer := <-n.nodeInfo.monitorChan
if peer == nil {
log.Info("monitorChan close")
return
}
if !peer.version.IsSupport() {
//如果版本不支持,直接删除节点
log.Info("VersoinMonitor", "NotSupport,addr", peer.Addr())
n.destroyPeer(peer)
//加入黑名单12小时
n.nodeInfo.blacklist.Add(peer.Addr(), int64(3600*12))
continue
}
if peer.IsMaxInbouds {
n.destroyPeer(peer)
}
if !peer.GetRunning() {
n.destroyPeer(peer)
continue
}
pstat, ok := n.nodeInfo.addrBook.setAddrStat(peer.Addr(), peer.peerStat.IsOk())
if ok {
if pstat.GetAttempts() > maxAttemps {
log.Debug("monitorErrPeer", "over maxattamps", pstat.GetAttempts())
n.destroyPeer(peer)
}
}
}
}
func (n *Node) getAddrFromGithub() {
if !n.nodeInfo.cfg.UseGithub {
return
}
//从github 上下载种子节点文件
res, err := http.Get("https://raw.githubusercontent.com/chainseed/seeds/master/bty.txt")
if err != nil {
log.Error("getAddrFromGithub", "http.Get", err.Error())
return
}
bf := new(bytes.Buffer)
_, err = io.Copy(bf, res.Body)
if err != nil {
log.Error("getAddrFromGithub", "io.Copy", err.Error())
return
}
fileContent := bf.String()
st := strings.TrimSpace(fileContent)
strs := strings.Split(st, "\n")
log.Info("getAddrFromGithub", "download file", fileContent)
for _, linestr := range strs {
pidaddr := strings.Split(linestr, "@")
if len(pidaddr) == 2 {
addr := pidaddr[1]
if n.Has(addr) || n.nodeInfo.blacklist.Has(addr) {
return
}
n.pubsub.FIFOPub(addr, "addr")
}
}
}
// getAddrFromOnline gets the address list from the online node
func (n *Node) getAddrFromOnline() {
ticker := time.NewTicker(GetAddrFromOnlineInterval)
defer ticker.Stop()
pcli := NewNormalP2PCli()
var ticktimes int
for {
<-ticker.C
seedsMap := make(map[string]bool)
//每次循环seed的排序不同
seedArr := n.nodeInfo.cfg.Seeds
for _, seed := range seedArr {
seedsMap[seed] = true
}
ticktimes++
if n.isClose() {
log.Debug("GetAddrFromOnLine", "loop", "done")
return
}
if n.Size() == 0 && ticktimes > 2 {
//尝试与Seed 进行连接
var rangeCount int
for addr := range seedsMap {
//先把seed 排除在外
rangeCount++
if rangeCount < maxOutBoundNum {
n.pubsub.FIFOPub(addr, "addr")
}
}
if rangeCount < maxOutBoundNum {
//从innerSeeds 读取连接
n.innerSeeds.Range(func(k, v interface{}) bool {
rangeCount++
if rangeCount < maxOutBoundNum {
n.pubsub.FIFOPub(k.(string), "addr")
return true
}
return false
})
}
continue
}
peers, _ := n.GetActivePeers()
for _, peer := range peers { //向其他节点发起请求,获取地址列表
var addrlist []string
var addrlistMap map[string]int64
var err error
addrlistMap, err = pcli.GetAddrList(peer)
P2pComm.CollectPeerStat(err, peer)
if err != nil {
log.Error("getAddrFromOnline", "ERROR", err.Error())
continue
}
for addr := range addrlistMap {
addrlist = append(addrlist, addr)
}
for _, addr := range addrlist {
if !n.needMore() {
//如果已经达到25个节点,则优先删除种子节点
localBlockHeight, err := pcli.GetBlockHeight(n.nodeInfo)
if err != nil {
continue
}
//查询对方的高度,如果不小于自己的高度,或高度差在一定范围内,则剔除一个种子
if peerHeight, ok := addrlistMap[addr]; ok {
if localBlockHeight-peerHeight < 1024 {
if _, ok := seedsMap[addr]; ok {
continue
}
//随机删除连接的一个种子
n.innerSeeds.Range(func(k, v interface{}) bool {
if n.Has(k.(string)) {
//不能包含在cfgseed中
if _, ok := n.cfgSeeds.Load(k.(string)); ok {
return true
}
n.remove(k.(string))
n.nodeInfo.addrBook.RemoveAddr(k.(string))
return false
}
return true
})
}
}
time.Sleep(MonitorPeerInfoInterval)
continue
}
if !n.nodeInfo.blacklist.Has(addr) || !peerAddrFilter.Contains(addr) {
if ticktimes < 10 {
//如果连接了其他节点,优先不连接种子节点
if _, ok := n.innerSeeds.Load(addr); !ok {
//先把seed 排除在外
n.pubsub.FIFOPub(addr, "addr")
}
} else {
n.pubsub.FIFOPub(addr, "addr")
}
}
}
}
}
}
func (n *Node) getAddrFromAddrBook() {
ticker := time.NewTicker(GetAddrFromAddrBookInterval)
defer ticker.Stop()
var tickerTimes int64
for {
<-ticker.C
tickerTimes++
if n.isClose() {
log.Debug("GetAddrFromOnLine", "loop", "done")
return
}
//12个循环后, 则去github下载
if tickerTimes > 12 && n.Size() == 0 {
n.getAddrFromGithub()
tickerTimes = 0
}
log.Debug("OUTBOUND NUM", "NUM", n.Size(), "start getaddr from peer,peernum", len(n.nodeInfo.addrBook.GetPeers()))
addrNetArr := n.nodeInfo.addrBook.GetPeers()
for _, addr := range addrNetArr {
if !n.Has(addr.String()) && !n.nodeInfo.blacklist.Has(addr.String()) {
log.Debug("GetAddrFromOffline", "Add addr", addr.String())
if n.needMore() || n.CacheBoundsSize() < maxOutBoundNum {
n.pubsub.FIFOPub(addr.String(), "addr")
}
}
}
log.Debug("Node Monitor process", "outbound num", n.Size())
}
}
func (n *Node) nodeReBalance() {
p2pcli := NewNormalP2PCli()
ticker := time.NewTicker(MonitorReBalanceInterval)
defer ticker.Stop()
for {
if n.isClose() {
log.Debug("nodeReBalance", "loop", "done")
return
}
<-ticker.C
log.Info("nodeReBalance", "cacheSize", n.CacheBoundsSize())
if n.CacheBoundsSize() == 0 {
continue
}
peers, _ := n.GetActivePeers()
//选出当前连接的节点中,负载最大的节点
var MaxInBounds int32
var MaxInBoundPeer *Peer
for _, peer := range peers {
if peer.GetInBouns() > MaxInBounds {
MaxInBounds = peer.GetInBouns()
MaxInBoundPeer = peer
}
}
if MaxInBoundPeer == nil {
continue
}
//筛选缓存备选节点负载最大和最小的节点
cachePeers := n.GetCacheBounds()
var MinCacheInBounds int32 = 1000
var MinCacheInBoundPeer *Peer
var MaxCacheInBounds int32
var MaxCacheInBoundPeer *Peer
for _, peer := range cachePeers {
inbounds, err := p2pcli.GetInPeersNum(peer)
if err != nil {
n.RemoveCachePeer(peer.Addr())
peer.Close()
continue
}
//选出最小负载
if int32(inbounds) < MinCacheInBounds {
MinCacheInBounds = int32(inbounds)
MinCacheInBoundPeer = peer
}
//选出负载最大
if int32(inbounds) > MaxCacheInBounds {
MaxCacheInBounds = int32(inbounds)
MaxCacheInBoundPeer = peer
}
}
if MinCacheInBoundPeer == nil || MaxCacheInBoundPeer == nil {
continue
}
//如果连接的节点最大负载量小于当前缓存节点的最大负载量
if MaxInBounds < MaxCacheInBounds {
n.RemoveCachePeer(MaxCacheInBoundPeer.Addr())
MaxCacheInBoundPeer.Close()
}
//如果最大的负载量比缓存中负载最小的小,则删除缓存中所有的节点
if MaxInBounds < MinCacheInBounds {
cachePeers := n.GetCacheBounds()
for _, peer := range cachePeers {
n.RemoveCachePeer(peer.Addr())
peer.Close()
}
continue
}
log.Info("nodeReBalance", "MaxInBounds", MaxInBounds, "MixCacheInBounds", MinCacheInBounds)
if MaxInBounds-MinCacheInBounds < 50 {
continue
}
if MinCacheInBoundPeer != nil {
info, err := MinCacheInBoundPeer.GetPeerInfo()
if err != nil {
n.RemoveCachePeer(MinCacheInBoundPeer.Addr())
MinCacheInBoundPeer.Close()
continue
}
localBlockHeight, err := p2pcli.GetBlockHeight(n.nodeInfo)
if err != nil {
continue
}
peerBlockHeight := info.GetHeader().GetHeight()
if localBlockHeight-peerBlockHeight < 2048 {
log.Info("noReBalance", "Repalce node new node", MinCacheInBoundPeer.Addr(), "old node", MaxInBoundPeer.Addr())
n.addPeer(MinCacheInBoundPeer)
n.nodeInfo.addrBook.AddAddress(MinCacheInBoundPeer.peerAddr, nil)
n.remove(MaxInBoundPeer.Addr())
n.RemoveCachePeer(MinCacheInBoundPeer.Addr())
}
}
}
}
func (n *Node) monitorPeers() {
p2pcli := NewNormalP2PCli()
ticker := time.NewTicker(MonitorPeerNumInterval)
defer ticker.Stop()
_, selfName := n.nodeInfo.addrBook.GetPrivPubKey()
for {
if n.isClose() {
log.Debug("monitorPeers", "loop", "done")
return
}
<-ticker.C
localBlockHeight, err := p2pcli.GetBlockHeight(n.nodeInfo)
if err != nil {
continue
}
peers, infos := n.GetActivePeers()
for name, pinfo := range infos {
peerheight := pinfo.GetHeader().GetHeight()
paddr := pinfo.GetAddr()
if name == selfName && !pinfo.GetSelf() { //发现连接到自己,立即删除
//删除节点数过低的节点
n.remove(pinfo.GetAddr())
n.nodeInfo.addrBook.RemoveAddr(paddr)
n.nodeInfo.blacklist.Add(paddr, 0)
}
if localBlockHeight-peerheight > 2048 {
//删除比自己较低的节点
if addrMap, err := p2pcli.GetAddrList(peers[paddr]); err == nil {
for addr := range addrMap {
if !n.Has(addr) && !n.nodeInfo.blacklist.Has(addr) {
n.pubsub.FIFOPub(addr, "addr")
}
}
}
if n.Size() <= stableBoundNum {
continue
}
//如果是配置节点,则不删除
if _, ok := n.cfgSeeds.Load(paddr); ok {
continue
}
//删除节点数过低的节点
n.remove(paddr)
n.nodeInfo.addrBook.RemoveAddr(paddr)
}
}
}
}
func (n *Node) monitorPeerInfo() {
go func() {
n.nodeInfo.FetchPeerInfo(n)
ticker := time.NewTicker(MonitorPeerInfoInterval)
defer ticker.Stop()
for {
if n.isClose() {
return
}
<-ticker.C
n.nodeInfo.FetchPeerInfo(n)
}
}()
}
// monitorDialPeers connect the node address concurrently
func (n *Node) monitorDialPeers() {
var dialCount int
addrChan := n.pubsub.Sub("addr")
p2pcli := NewNormalP2PCli()
for addr := range addrChan {
if n.isClose() {
log.Info("monitorDialPeers", "loop", "done")
return
}
if peerAddrFilter.Contains(addr.(string)) {
//先查询有没有注册进去,避免同时重复连接相同的地址
continue
}
netAddr, err := NewNetAddressString(addr.(string))
if err != nil {
continue
}
if n.nodeInfo.addrBook.ISOurAddress(netAddr) {
continue
}
//不对已经连接上的地址或者黑名单地址发起连接 TODO:连接足够时,对于连入的地址也不再去重复连接(客户端服务端只维护一条连接, 后续优化)
if n.Has(netAddr.String()) || n.nodeInfo.blacklist.Has(netAddr.String()) || n.HasCacheBound(netAddr.String()) {
log.Debug("DialPeers", "find hash", netAddr.String())
continue
}
//注册的节点超过最大节点数暂不连接
if !n.needMore() && n.CacheBoundsSize() >= maxOutBoundNum {
n.pubsub.FIFOPub(addr, "addr")
time.Sleep(time.Second * 10)
continue
}
log.Debug("DialPeers", "peer", netAddr.String())
//并发连接节点,增加连接效率
if dialCount >= maxOutBoundNum*2 {
n.pubsub.FIFOPub(addr, "addr")
time.Sleep(time.Second * 10)
dialCount = len(n.GetRegisterPeers()) + n.CacheBoundsSize()
continue
}
dialCount++
//把待连接的节点增加到过滤容器中
peerAddrFilter.Add(addr.(string), types.Now().Unix())
log.Debug("monitorDialPeer", "dialCount", dialCount)
go func(netAddr *NetAddress) {
defer peerAddrFilter.Remove(netAddr.String())
peer, err := P2pComm.dialPeer(netAddr, n)
if err != nil {
//连接失败后
n.nodeInfo.addrBook.RemoveAddr(netAddr.String())
log.Error("monitorDialPeers", "peerAddr", netAddr.str, "err", err.Error())
if err == types.ErrVersion { //版本不支持,加入黑名单12小时
peer.version.SetSupport(false)
P2pComm.CollectPeerStat(err, peer)
return
}
//其他原因,黑名单10分钟
if peer != nil {
peer.Close()
}
if _, ok := n.cfgSeeds.Load(netAddr.String()); !ok {
n.nodeInfo.blacklist.Add(netAddr.String(), int64(60*10))
}
return
}
//查询远程节点的负载
inbounds, err := p2pcli.GetInPeersNum(peer)
if err != nil {
peer.Close()
return
}
//判断负载情况,负载达到额定90%,负载过大,就删除节点
if int32(inbounds*100)/n.nodeInfo.cfg.InnerBounds > 90 {
peer.Close()
return
}
//注册的节点超过最大节点数暂不连接
if len(n.GetRegisterPeers()) >= maxOutBoundNum {
if n.CacheBoundsSize() < maxOutBoundNum {
n.AddCachePeer(peer)
} else {
peer.Close()
}
return
}
n.addPeer(peer)
n.nodeInfo.addrBook.AddAddress(netAddr, nil)
}(netAddr)
}
}
func (n *Node) monitorBlackList() {
ticker := time.NewTicker(CheckBlackListInterVal)
defer ticker.Stop()
for {
if n.isClose() {
log.Info("monitorBlackList", "loop", "done")
return
}
<-ticker.C
badPeers := n.nodeInfo.blacklist.GetBadPeers()
now := types.Now().Unix()
for badPeer, intime := range badPeers {
if n.nodeInfo.addrBook.IsOurStringAddress(badPeer) {
continue
}
//0表示永久加入黑名单
if 0 == intime {
continue
}
if now > intime {
n.nodeInfo.blacklist.Delete(badPeer)
}
}
}
}
func (n *Node) monitorFilter() {
tickTime := time.Second * 30
peerAddrFilter.ManageRecvFilter(tickTime)
}
//独立goroutine 监控配置的
func (n *Node) monitorCfgSeeds() {
ticker := time.NewTicker(CheckCfgSeedsInterVal)
defer ticker.Stop()
for {
if n.isClose() {
log.Info("monitorCfgSeeds", "loop", "done")
return
}
<-ticker.C
n.cfgSeeds.Range(func(k, v interface{}) bool {
if !n.Has(k.(string)) {
//尝试连接此节点
if n.needMore() { //如果需要更多的节点
n.pubsub.FIFOPub(k.(string), "addr")
} else {
//腾笼换鸟
peers, _ := n.GetActivePeers()
//选出当前连接的节点中,负载最大的节点
var MaxInBounds int32
MaxInBoundPeer := &Peer{}
for _, peer := range peers {
if peer.GetInBouns() > MaxInBounds {
MaxInBounds = peer.GetInBouns()
MaxInBoundPeer = peer
}
}
n.remove(MaxInBoundPeer.Addr())
n.pubsub.FIFOPub(k.(string), "addr")
}
}
return true
})
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
// Package nat provides access to common network port mapping protocols.
package nat
import (
"errors"
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/33cn/chain33/common/log/log15"
natpmp "github.com/jackpal/go-nat-pmp"
)
var (
log = log15.New("module", "p2p.nat")
)
// Interface An implementation of nat.Interface can map local ports to ports
// accessible from the Internet.
type Interface interface {
// These methods manage a mapping between a port on the local
// machine to a port that can be connected to from the internet.
//
// protocol is "UDP" or "TCP". Some implementations allow setting
// a display name for the mapping. The mapping may be removed by
// the gateway when its lifetime ends.
AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error
DeleteMapping(protocol string, extport, intport int) error
// This method should return the external (Internet-facing)
// address of the gateway device.
ExternalIP() (net.IP, error)
// Should return name of the method. This is used for logging.
String() string
}
// Parse parses a NAT interface description.
// The following formats are currently accepted.
// Note that mechanism names are not case-sensitive.
//
// "" or "none" return nil
// "extip:77.12.33.4" will assume the local machine is reachable on the given IP
// "any" uses the first auto-detected mechanism
// "upnp" uses the Universal Plug and Play protocol
// "pmp" uses NAT-PMP with an auto-detected gateway address
// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
func Parse(spec string) (Interface, error) {
var (
parts = strings.SplitN(spec, ":", 2)
mech = strings.ToLower(parts[0])
ip net.IP
)
if len(parts) > 1 {
ip = net.ParseIP(parts[1])
if ip == nil {
return nil, errors.New("invalid IP address")
}
}
switch mech {
case "", "none", "off":
return nil, nil
case "any", "auto", "on":
return Any(), nil
case "extip", "ip":
if ip == nil {
return nil, errors.New("missing IP address")
}
return ExtIP(ip), nil
case "upnp":
return UPnP(), nil
case "pmp", "natpmp", "nat-pmp":
return PMP(ip), nil
default:
return nil, fmt.Errorf("unknown mechanism %q", parts[0])
}
}
const (
mapTimeout = 20 * time.Minute
mapUpdateInterval = 15 * time.Minute
)
// Map adds a port mapping on m and keeps it alive until c is closed.
// This function is typically invoked in its own goroutine.
// Map adds a port mapping on m and keeps it alive until c is closed.
// This function is typically invoked in its own goroutine.
func Map(m Interface, c chan struct{}, protocol string, extport, intport int, name string) {
refresh := time.NewTimer(mapUpdateInterval)
defer func() {
refresh.Stop()
log.Info("Deleting port mapping")
err := m.DeleteMapping(protocol, extport, intport)
if err != nil {
return
}
}()
if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil {
log.Error("Couldn't add port mapping", "err", err)
} else {
log.Info("Mapped network port")
}
for {
select {
case _, ok := <-c:
if !ok {
return
}
case <-refresh.C:
log.Info("Refreshing port mapping")
if err := m.AddMapping(protocol, extport, intport, name, mapTimeout); err != nil {
log.Error("Couldn't add port mapping", "err", err)
}
refresh.Reset(mapUpdateInterval)
}
}
}
// ExtIP assumes that the local machine is reachable on the given
// external IP address, and that any required ports were mapped manually.
// Mapping operations will not return an error but won't actually do anything.
func ExtIP(ip net.IP) Interface {
if ip == nil {
panic("IP must not be nil")
}
return extIP(ip)
}
type extIP net.IP
func (n extIP) ExternalIP() (net.IP, error) { return net.IP(n), nil }
func (n extIP) String() string { return fmt.Sprintf("ExtIP(%v)", net.IP(n)) }
// These do nothing.
func (extIP) AddMapping(string, int, int, string, time.Duration) error { return nil }
func (extIP) DeleteMapping(string, int, int) error { return nil }
// Any returns a port mapper that tries to discover any supported
// mechanism on the local network.
func Any() Interface {
// TODO: attempt to discover whether the local machine has an
// Internet-class address. Return ExtIP in this case.
return startautodisc("UPnP or NAT-PMP", func() Interface {
found := make(chan Interface, 2)
go func() { found <- discoverUPnP() }()
go func() { found <- discoverPMP() }()
for i := 0; i < cap(found); i++ {
if c := <-found; c != nil {
return c
}
}
return nil
})
}
// UPnP returns a port mapper that uses UPnP. It will attempt to
// discover the address of your router using UDP broadcasts.
func UPnP() Interface {
return startautodisc("UPnP", discoverUPnP)
}
// PMP returns a port mapper that uses NAT-PMP. The provided gateway
// address should be the IP of your router. If the given gateway
// address is nil, PMP will attempt to auto-discover the router.
func PMP(gateway net.IP) Interface {
if gateway != nil {
return &pmp{gw: gateway, c: natpmp.NewClient(gateway)}
}
return startautodisc("NAT-PMP", discoverPMP)
}
// autodisc represents a port mapping mechanism that is still being
// auto-discovered. Calls to the Interface methods on this type will
// wait until the discovery is done and then call the method on the
// discovered mechanism.
//
// This type is useful because discovery can take a while but we
// want return an Interface value from UPnP, PMP and Auto immediately.
type autodisc struct {
what string // type of interface being autodiscovered
once sync.Once
doit func() Interface
mu sync.Mutex
found Interface
}
func startautodisc(what string, doit func() Interface) Interface {
// TODO: monitor network configuration and rerun doit when it changes.
return &autodisc{what: what, doit: doit}
}
func (n *autodisc) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error {
if err := n.wait(); err != nil {
return err
}
return n.found.AddMapping(protocol, extport, intport, name, lifetime)
}
func (n *autodisc) DeleteMapping(protocol string, extport, intport int) error {
if err := n.wait(); err != nil {
return err
}
return n.found.DeleteMapping(protocol, extport, intport)
}
func (n *autodisc) ExternalIP() (net.IP, error) {
if err := n.wait(); err != nil {
return nil, err
}
return n.found.ExternalIP()
}
func (n *autodisc) String() string {
n.mu.Lock()
defer n.mu.Unlock()
if n.found == nil {
return n.what
}
return n.found.String()
}
// wait blocks until auto-discovery has been performed.
func (n *autodisc) wait() error {
n.once.Do(func() {
n.mu.Lock()
n.found = n.doit()
n.mu.Unlock()
})
if n.found == nil {
return fmt.Errorf("no %s router discovered", n.what)
}
return nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
package nat
import (
"net"
"testing"
"time"
)
// This test checks that autodisc doesn't hang and returns
// consistent results when multiple goroutines call its methods
// concurrently.
func TestAutoDiscRace(t *testing.T) {
ad := startautodisc("thing", func() Interface {
time.Sleep(500 * time.Millisecond)
return extIP{33, 44, 55, 66}
})
// Spawn a few concurrent calls to ad.ExternalIP.
type rval struct {
ip net.IP
err error
}
results := make(chan rval, 50)
for i := 0; i < cap(results); i++ {
go func() {
ip, err := ad.ExternalIP()
results <- rval{ip, err}
}()
}
// Check that they all return the correct result within the deadline.
deadline := time.After(2 * time.Second)
for i := 0; i < cap(results); i++ {
select {
case <-deadline:
t.Fatal("deadline exceeded")
case rval := <-results:
if rval.err != nil {
t.Errorf("result %d: unexpected error: %v", i, rval.err)
}
wantIP := net.IP{33, 44, 55, 66}
if !rval.ip.Equal(wantIP) {
t.Errorf("result %d: got IP %v, want %v", i, rval.ip, wantIP)
}
}
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
package nat
import (
"fmt"
"net"
"strings"
"time"
natpmp "github.com/jackpal/go-nat-pmp"
)
// natPMPClient adapts the NAT-PMP protocol implementation so it conforms to
// the common interface.
type pmp struct {
gw net.IP
c *natpmp.Client
}
func (n *pmp) String() string {
return fmt.Sprintf("NAT-PMP(%v)", n.gw)
}
func (n *pmp) ExternalIP() (net.IP, error) {
response, err := n.c.GetExternalAddress()
if err != nil {
return nil, err
}
return response.ExternalIPAddress[:], nil
}
func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error {
if lifetime <= 0 {
return fmt.Errorf("lifetime must not be <= 0")
}
// Note order of port arguments is switched between our
// AddMapping and the client's AddPortMapping.
_, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
return err
}
func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) {
// To destroy a mapping, send an add-port with an internalPort of
// the internal port to destroy, an external port of zero and a
// time of zero.
_, err = n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0)
return err
}
func discoverPMP() Interface {
// run external address lookups on all potential gateways
gws := potentialGateways()
found := make(chan *pmp, len(gws))
for i := range gws {
gw := gws[i]
go func() {
c := natpmp.NewClient(gw)
if _, err := c.GetExternalAddress(); err != nil {
found <- nil
} else {
found <- &pmp{gw, c}
}
}()
}
// return the one that responds first.
// discovery needs to be quick, so we stop caring about
// any responses after a very short timeout.
timeout := time.NewTimer(1 * time.Second)
defer timeout.Stop()
for range gws {
select {
case c := <-found:
if c != nil {
return c
}
case <-timeout.C:
return nil
}
}
return nil
}
var (
// LAN IP ranges
_, lan10, _ = net.ParseCIDR("10.0.0.0/8")
_, lan176, _ = net.ParseCIDR("172.16.0.0/12")
_, lan192, _ = net.ParseCIDR("192.168.0.0/16")
)
// TODO: improve this. We currently assume that (on most networks)
// the router is X.X.X.1 in a local LAN range.
func potentialGateways() (gws []net.IP) {
ifaces, err := net.Interfaces()
if err != nil {
return nil
}
for _, iface := range ifaces {
ifaddrs, err := iface.Addrs()
if err != nil {
return gws
}
for _, addr := range ifaddrs {
switch x := addr.(type) {
case *net.IPNet:
if lan10.Contains(x.IP) || lan176.Contains(x.IP) || lan192.Contains(x.IP) {
ip := x.IP.Mask(x.Mask).To4()
if ip != nil {
ip[3] = ip[3] | 0x01
gws = append(gws, ip)
}
}
}
}
}
return gws
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
package nat
import (
"errors"
"fmt"
"net"
"strings"
"time"
"github.com/huin/goupnp"
"github.com/huin/goupnp/dcps/internetgateway1"
"github.com/huin/goupnp/dcps/internetgateway2"
)
const soapRequestTimeout = 3 * time.Second
type upnp struct {
dev *goupnp.RootDevice
service string
client upnpClient
}
type upnpClient interface {
GetExternalIPAddress() (string, error)
AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
DeletePortMapping(string, uint16, string) error
GetNATRSIPStatus() (sip bool, nat bool, err error)
}
func (n *upnp) ExternalIP() (addr net.IP, err error) {
ipString, err := n.client.GetExternalIPAddress()
if err != nil {
return nil, err
}
ip := net.ParseIP(ipString)
if ip == nil {
return nil, errors.New("bad IP in response")
}
return ip, nil
}
func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error {
ip, err := n.internalAddress()
if err != nil {
return nil
}
protocol = strings.ToUpper(protocol)
lifetimeS := uint32(lifetime / time.Second)
err = n.DeleteMapping(protocol, extport, intport)
if err != nil {
return err
}
return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
}
func (n *upnp) internalAddress() (net.IP, error) {
devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
if err != nil {
return nil, err
}
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
switch x := addr.(type) {
case *net.IPNet:
if x.Contains(devaddr.IP) {
return x.IP, nil
}
}
}
}
return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
}
func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
}
func (n *upnp) String() string {
return "UPNP " + n.service
}
// discoverUPnP searches for Internet Gateway Devices
// and returns the first one it can find on the local network.
func discoverUPnP() Interface {
found := make(chan *upnp, 2)
// IGDv1
go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
switch sc.Service.ServiceType {
case internetgateway1.URN_WANIPConnection_1:
return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{ServiceClient: sc}}
case internetgateway1.URN_WANPPPConnection_1:
return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{ServiceClient: sc}}
}
return nil
})
// IGDv2
go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
switch sc.Service.ServiceType {
case internetgateway2.URN_WANIPConnection_1:
return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{ServiceClient: sc}}
case internetgateway2.URN_WANIPConnection_2:
return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{ServiceClient: sc}}
case internetgateway2.URN_WANPPPConnection_1:
return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{ServiceClient: sc}}
}
return nil
})
for i := 0; i < cap(found); i++ {
if c := <-found; c != nil {
return c
}
}
return nil
}
// finds devices matching the given target and calls matcher for all
// advertised services of each device. The first non-nil service found
// is sent into out. If no service matched, nil is sent.
func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) {
devs, err := goupnp.DiscoverDevices(target)
if err != nil {
out <- nil
return
}
found := false
for i := 0; i < len(devs) && !found; i++ {
if devs[i].Root == nil {
continue
}
devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
if found {
return
}
// check for a matching IGD service
sc := goupnp.ServiceClient{
SOAPClient: service.NewSOAPClient(),
RootDevice: devs[i].Root,
Location: devs[i].Location,
Service: service,
}
sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout
upnp := matcher(devs[i].Root, sc)
if upnp == nil {
return
}
// check whether port mapping is enabled
if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat {
return
}
out <- upnp
found = true
})
}
if !found {
out <- nil
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2015 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 <http://www.gnu.org/licenses/>.
package nat
import (
"fmt"
"io"
"net"
"net/http"
"strings"
"testing"
"github.com/huin/goupnp/httpu"
)
// fakeIGD presents itself as a discoverable UPnP device which sends
// canned responses to HTTPU and HTTP requests.
type fakeIGD struct {
t *testing.T // for logging
listener net.Listener
mcastListener *net.UDPConn
// This should be a complete HTTP response (including headers).
// It is sent as the response to any sspd packet. Any occurrence
// of "{{listenAddr}}" is replaced with the actual TCP listen
// address of the HTTP server.
ssdpResp string
// This one should contain XML payloads for all requests
// performed. The keys contain method and path, e.g. "GET /foo/bar".
// As with ssdpResp, "{{listenAddr}}" is replaced with the TCP
// listen address.
httpResps map[string]string
}
// httpu.Handler
func (dev *fakeIGD) ServeMessage(r *http.Request) {
dev.t.Logf(`HTTPU request %s %s`, r.Method, r.RequestURI)
conn, err := net.Dial("udp4", r.RemoteAddr)
if err != nil {
fmt.Printf("reply Dial error: %v", err)
return
}
defer conn.Close()
io.WriteString(conn, dev.replaceListenAddr(dev.ssdpResp))
}
// http.Handler
func (dev *fakeIGD) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if resp, ok := dev.httpResps[r.Method+" "+r.RequestURI]; ok {
dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 200)
io.WriteString(w, dev.replaceListenAddr(resp))
} else {
dev.t.Logf(`HTTP request "%s %s" --> %d`, r.Method, r.RequestURI, 404)
w.WriteHeader(http.StatusNotFound)
}
}
func (dev *fakeIGD) replaceListenAddr(resp string) string {
return strings.Replace(resp, "{{listenAddr}}", dev.listener.Addr().String(), -1)
}
func (dev *fakeIGD) listen() (err error) {
if dev.listener, err = net.Listen("tcp", "127.0.0.1:0"); err != nil {
return err
}
laddr := &net.UDPAddr{IP: net.ParseIP("239.255.255.250"), Port: 1900}
if dev.mcastListener, err = net.ListenMulticastUDP("udp", nil, laddr); err != nil {
dev.listener.Close()
return err
}
return nil
}
func (dev *fakeIGD) serve() {
go httpu.Serve(dev.mcastListener, dev)
go http.Serve(dev.listener, dev)
}
func (dev *fakeIGD) close() {
dev.mcastListener.Close()
dev.listener.Close()
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"net"
"testing"
"github.com/33cn/chain33/p2p/utils"
"github.com/stretchr/testify/assert"
)
func TestNetAddress(t *testing.T) {
tcpAddr := new(net.TCPAddr)
tcpAddr.IP = net.ParseIP("localhost")
tcpAddr.Port = 2223
nad := NewNetAddress(tcpAddr)
nad1 := nad.Copy()
nad.Equals(nad1)
nad2s, err := NewNetAddressStrings([]string{"localhost:3306"})
if err != nil {
return
}
nad.Less(nad2s[0])
}
func TestAddrRouteble(t *testing.T) {
resp := P2pComm.AddrRouteble([]string{"114.55.101.159:13802"}, utils.CalcChannelVersion(119, VERSION))
t.Log(resp)
}
func TestGetLocalAddr(t *testing.T) {
t.Log(P2pComm.GetLocalAddr())
}
func TestP2pListen(t *testing.T) {
var node Node
node.listenPort = 3333
listen1 := newListener("tcp", &node)
assert.Equal(t, true, listen1 != nil)
listen2 := newListener("tcp", &node)
assert.Equal(t, true, listen2 != nil)
listen1.Close()
listen2.Close()
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"context"
"fmt"
"net"
"strconv"
"time"
pb "github.com/33cn/chain33/types"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/keepalive"
)
// NetAddress defines information about a peer on the network
// including its IP address, and port.
type NetAddress struct {
IP net.IP
Port uint16
str string
}
// NewNetAddress returns a new NetAddress using the provided TCP
// address.
func NewNetAddress(addr net.Addr) *NetAddress {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return nil
}
ip := tcpAddr.IP
port := uint16(tcpAddr.Port)
return NewNetAddressIPPort(ip, port)
}
// NewNetAddressString returns a new NetAddress using the provided
// address in the form of "IP:Port". Also resolves the host if host
// is not an IP.
func NewNetAddressString(addr string) (*NetAddress, error) {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
ip := net.ParseIP(host)
if ip == nil {
if len(host) > 0 {
ips, err := net.LookupIP(host)
if err != nil {
return nil, err
}
ip = ips[0]
}
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil, err
}
na := NewNetAddressIPPort(ip, uint16(port))
return na, nil
}
// NewNetAddressStrings returns an array of NetAddress'es build using
// the provided strings.
func NewNetAddressStrings(addrs []string) ([]*NetAddress, error) {
netAddrs := make([]*NetAddress, len(addrs))
for i, addr := range addrs {
netAddr, err := NewNetAddressString(addr)
if err != nil {
return nil, fmt.Errorf("error in address %s: %v", addr, err)
}
netAddrs[i] = netAddr
}
return netAddrs, nil
}
// NewNetAddressIPPort returns a new NetAddress using the provided IP
// and port number.
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
na := &NetAddress{
IP: ip,
Port: port,
str: net.JoinHostPort(
ip.String(),
strconv.FormatUint(uint64(port), 10),
),
}
return na
}
// Equals reports whether na and other are the same addresses.
func (na *NetAddress) Equals(other interface{}) bool {
if o, ok := other.(*NetAddress); ok {
return na.String() == o.String()
}
return false
}
// Less reports whether na and other are the less addresses
func (na *NetAddress) Less(other interface{}) bool {
if o, ok := other.(*NetAddress); ok {
return na.String() < o.String()
}
log.Error("Cannot compare unequal types")
return false
}
// String representation.
func (na *NetAddress) String() string {
if na.str == "" {
na.str = net.JoinHostPort(
na.IP.String(),
strconv.FormatUint(uint64(na.Port), 10),
)
}
return na.str
}
// Copy na address
func (na *NetAddress) Copy() *NetAddress {
copytmp := *na
return &copytmp
}
// DialTimeout calls net.DialTimeout on the address.
func isCompressSupport(err error) bool {
var errstr = `grpc: Decompressor is not installed for grpc-encoding "gzip"`
if grpc.Code(err) == codes.Unimplemented && grpc.ErrorDesc(err) == errstr {
return false
}
return true
}
// DialTimeout dial timeout
func (na *NetAddress) DialTimeout(version int32) (*grpc.ClientConn, error) {
ch := make(chan grpc.ServiceConfig, 1)
ch <- P2pComm.GrpcConfig()
var cliparm keepalive.ClientParameters
cliparm.Time = 15 * time.Second //keepalive ping 周期
cliparm.Timeout = 10 * time.Second //ping后的获取ack消息超时时间
cliparm.PermitWithoutStream = true //启动keepalive 进行检查
keepaliveOp := grpc.WithKeepaliveParams(cliparm)
timeoutOp := grpc.WithTimeout(time.Second * 3)
log.Debug("NetAddress", "Dial", na.String())
conn, err := grpc.Dial(na.String(), grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")), grpc.WithServiceConfig(ch), keepaliveOp, timeoutOp)
if err != nil {
log.Debug("grpc DialCon", "did not connect", err, "addr", na.String())
return nil, err
}
//p2p version check 通过版本协议,获取通信session
//判断是否对方是否支持压缩
cli := pb.NewP2PgserviceClient(conn)
_, err = cli.GetHeaders(context.Background(), &pb.P2PGetHeaders{StartHeight: 0, EndHeight: 0, Version: version}, grpc.FailFast(true))
if err != nil && !isCompressSupport(err) {
//compress not support
log.Error("compress not supprot , rollback to uncompress version", "addr", na.String())
err = conn.Close()
if err != nil {
log.Error("conn", "close err", err)
}
ch2 := make(chan grpc.ServiceConfig, 1)
ch2 <- P2pComm.GrpcConfig()
log.Debug("NetAddress", "Dial with unCompressor", na.String())
conn, err = grpc.Dial(na.String(), grpc.WithInsecure(), grpc.WithServiceConfig(ch2), keepaliveOp, timeoutOp)
}
if err != nil {
log.Debug("grpc DialCon Uncompressor", "did not connect", err)
if conn != nil {
errs := conn.Close()
if errs != nil {
log.Error("conn", "close err", errs)
}
}
return nil, err
}
return conn, nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"fmt"
"math/rand"
"github.com/33cn/chain33/p2p"
//"strings"
"sync/atomic"
"sync"
"time"
"github.com/33cn/chain33/common/pubsub"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/p2p/gossip/nat"
)
// 启动Node节点
// 1.启动监听GRPC Server
// 2.检测自身地址
// 3.启动端口映射
// 4.启动监控模块,进行节点管理
// Start Node server
func (n *Node) Start() {
if n.server != nil {
n.server.Start()
}
n.detectNodeAddr()
n.monitor()
atomic.StoreInt32(&n.closed, 0)
go n.doNat()
}
// Close node server
func (n *Node) Close() {
//避免重复
if !atomic.CompareAndSwapInt32(&n.closed, 0, 1) {
return
}
if n.server != nil {
n.server.Close()
}
log.Debug("stop", "listen", "closed")
n.nodeInfo.addrBook.Close()
n.nodeInfo.monitorChan <- nil
log.Debug("stop", "addrBook", "closed")
n.removeAll()
if peerAddrFilter != nil {
peerAddrFilter.Close()
}
n.deleteNatMapPort()
n.pubsub.Shutdown()
log.Info("stop", "PeerRemoeAll", "closed")
}
func (n *Node) isClose() bool {
return atomic.LoadInt32(&n.closed) == 1
}
// Node attribute
type Node struct {
omtx sync.Mutex
nodeInfo *NodeInfo
cmtx sync.Mutex
cacheBound map[string]*Peer
outBound map[string]*Peer
server *listener
listenPort int
innerSeeds sync.Map
cfgSeeds sync.Map
closed int32
pubsub *pubsub.PubSub
chainCfg *types.Chain33Config
p2pMgr *p2p.Manager
}
// SetQueueClient return client for nodeinfo
func (n *Node) SetQueueClient(client queue.Client) {
n.nodeInfo.client = client
}
// NewNode produce a node object
func NewNode(mgr *p2p.Manager, mcfg *subConfig) (*Node, error) {
cfg := mgr.ChainCfg
node := &Node{
outBound: make(map[string]*Peer),
cacheBound: make(map[string]*Peer),
pubsub: pubsub.NewPubSub(10200),
p2pMgr: mgr,
}
node.listenPort = 13802
if mcfg.Port != 0 && mcfg.Port <= 65535 && mcfg.Port > 1024 {
node.listenPort = int(mcfg.Port)
}
if mcfg.InnerSeedEnable {
seeds := MainNetSeeds
if cfg.IsTestNet() {
seeds = TestNetSeeds
}
for _, seed := range seeds {
node.innerSeeds.Store(seed, "inner")
}
}
for _, seed := range mcfg.Seeds {
node.cfgSeeds.Store(seed, "cfg")
}
node.nodeInfo = NewNodeInfo(cfg.GetModuleConfig().P2P, mcfg)
if mcfg.ServerStart {
node.server = newListener(protocol, node)
}
node.chainCfg = cfg
return node, nil
}
func (n *Node) flushNodePort(localport, export uint16) {
if exaddr, err := NewNetAddressString(fmt.Sprintf("%v:%v", n.nodeInfo.GetExternalAddr().IP.String(), export)); err == nil {
n.nodeInfo.SetExternalAddr(exaddr)
n.nodeInfo.addrBook.AddOurAddress(exaddr)
}
if listenAddr, err := NewNetAddressString(fmt.Sprintf("%v:%v", n.nodeInfo.GetListenAddr().IP.String(), localport)); err == nil {
n.nodeInfo.SetListenAddr(listenAddr)
n.nodeInfo.addrBook.AddOurAddress(listenAddr)
}
}
func (n *Node) natOk() bool {
n.nodeInfo.natNoticeChain <- struct{}{}
ok := <-n.nodeInfo.natResultChain
return ok
}
func (n *Node) doNat() {
//测试是否在外网,当连接节点数大于0的时候,测试13802端口
for {
if n.Size() > 0 {
break
}
time.Sleep(time.Second)
}
testExaddr := fmt.Sprintf("%v:%v", n.nodeInfo.GetExternalAddr().IP.String(), n.listenPort)
log.Info("TestNetAddr", "testExaddr", testExaddr)
if len(P2pComm.AddrRouteble([]string{testExaddr}, n.nodeInfo.channelVersion)) != 0 {
log.Info("node outside")
n.nodeInfo.SetNetSide(true)
if netexaddr, err := NewNetAddressString(testExaddr); err == nil {
n.nodeInfo.SetExternalAddr(netexaddr)
n.nodeInfo.addrBook.AddOurAddress(netexaddr)
}
return
}
log.Info("node inside")
//在内网,并且非种子节点,则进行端口映射
if !n.nodeInfo.OutSide() && !n.nodeInfo.cfg.IsSeed && n.nodeInfo.cfg.ServerStart {
go n.natMapPort()
if !n.natOk() {
log.Info("doNat", "Nat", "Faild")
} else {
//检测映射成功后,能否对外提供服务
for {
if n.Size() > 0 {
break
}
time.Sleep(time.Millisecond * 100)
}
p2pcli := NewNormalP2PCli()
//测试映射后的端口能否连通或者外网+本地端口
if p2pcli.CheckPeerNatOk(n.nodeInfo.GetExternalAddr().String(), n.nodeInfo) ||
p2pcli.CheckPeerNatOk(fmt.Sprintf("%v:%v", n.nodeInfo.GetExternalAddr().IP.String(), n.listenPort), n.nodeInfo) {
n.nodeInfo.SetServiceTy(Service)
log.Info("doNat", "NatOk", "Support Service")
} else {
n.nodeInfo.SetServiceTy(Service - nodeNetwork)
log.Info("doNat", "NatOk", "No Support Service")
}
}
}
//n.nodeInfo.SetNatDone()
n.nodeInfo.addrBook.AddOurAddress(n.nodeInfo.GetExternalAddr())
n.nodeInfo.addrBook.AddOurAddress(n.nodeInfo.GetListenAddr())
if selefNet, err := NewNetAddressString(fmt.Sprintf("127.0.0.1:%v", n.nodeInfo.GetListenAddr().Port)); err == nil {
n.nodeInfo.addrBook.AddOurAddress(selefNet)
}
}
func (n *Node) addPeer(pr *Peer) {
n.omtx.Lock()
defer n.omtx.Unlock()
if peer, ok := n.outBound[pr.Addr()]; ok {
log.Info("AddPeer", "delete peer", pr.Addr())
n.nodeInfo.addrBook.RemoveAddr(peer.Addr())
delete(n.outBound, pr.Addr())
peer.Close()
}
log.Debug("AddPeer", "peer", pr.Addr())
n.outBound[pr.Addr()] = pr
pr.Start()
}
// AddCachePeer add cacheBound map by addr
func (n *Node) AddCachePeer(pr *Peer) {
n.cmtx.Lock()
defer n.cmtx.Unlock()
n.cacheBound[pr.Addr()] = pr
}
// RemoveCachePeer remove cacheBound by addr
func (n *Node) RemoveCachePeer(addr string) {
n.cmtx.Lock()
defer n.cmtx.Unlock()
delete(n.cacheBound, addr)
}
// HasCacheBound peer whether exists according to address
func (n *Node) HasCacheBound(addr string) bool {
n.cmtx.Lock()
defer n.cmtx.Unlock()
_, ok := n.cacheBound[addr]
return ok
}
// CacheBoundsSize return node cachebount size
func (n *Node) CacheBoundsSize() int {
n.cmtx.Lock()
defer n.cmtx.Unlock()
return len(n.cacheBound)
}
// GetCacheBounds get node cachebounds
func (n *Node) GetCacheBounds() []*Peer {
n.cmtx.Lock()
defer n.cmtx.Unlock()
var peers []*Peer
if len(n.cacheBound) == 0 {
return peers
}
for _, peer := range n.cacheBound {
peers = append(peers, peer)
}
return peers
}
// Size return size for peersize
func (n *Node) Size() int {
return n.nodeInfo.peerInfos.PeerSize()
}
// Has peer whether exists according to address
func (n *Node) Has(paddr string) bool {
n.omtx.Lock()
defer n.omtx.Unlock()
_, ok := n.outBound[paddr]
return ok
}
// GetRegisterPeer return one peer according to paddr
func (n *Node) GetRegisterPeer(paddr string) *Peer {
n.omtx.Lock()
defer n.omtx.Unlock()
if peer, ok := n.outBound[paddr]; ok {
return peer
}
return nil
}
// GetRegisterPeers return peers
func (n *Node) GetRegisterPeers() []*Peer {
n.omtx.Lock()
defer n.omtx.Unlock()
var peers []*Peer
if len(n.outBound) == 0 {
return peers
}
for _, peer := range n.outBound {
peers = append(peers, peer)
}
return peers
}
// GetActivePeers return activities of the peers and infos
func (n *Node) GetActivePeers() (map[string]*Peer, map[string]*types.Peer) {
regPeers := n.GetRegisterPeers()
infos := n.nodeInfo.peerInfos.GetPeerInfos()
var peers = make(map[string]*Peer)
for _, peer := range regPeers {
name := peer.GetPeerName()
if _, ok := infos[name]; ok {
peers[name] = peer
}
}
return peers, infos
}
func (n *Node) remove(peerAddr string) {
n.omtx.Lock()
defer n.omtx.Unlock()
peer, ok := n.outBound[peerAddr]
if ok {
delete(n.outBound, peerAddr)
peer.Close()
}
}
func (n *Node) removeAll() {
n.omtx.Lock()
defer n.omtx.Unlock()
for addr, peer := range n.outBound {
delete(n.outBound, addr)
peer.Close()
}
}
func (n *Node) monitor() {
go n.monitorErrPeer()
//固定模式, 只连接seeds配置节点
go n.monitorCfgSeeds()
if !n.nodeInfo.cfg.FixedSeed {
go n.getAddrFromOnline()
go n.getAddrFromAddrBook()
}
go n.monitorPeerInfo()
go n.monitorDialPeers()
go n.monitorBlackList()
go n.monitorFilter()
go n.monitorPeers()
go n.nodeReBalance()
}
func (n *Node) needMore() bool {
outBoundNum := n.Size()
return !(outBoundNum >= maxOutBoundNum)
}
func (n *Node) detectNodeAddr() {
var externalIP string
for {
cfg := n.nodeInfo.cfg
laddr := P2pComm.GetLocalAddr()
//LocalAddr = laddr
log.Info("DetectNodeAddr", "addr:", laddr)
if laddr == "" {
log.Error("DetectNodeAddr", "NetWork Disable p2p Disable", "Retry until Network enable")
time.Sleep(time.Second * 5)
continue
}
log.Info("detectNodeAddr", "LocalAddr", laddr)
if cfg.IsSeed {
log.Info("DetectNodeAddr", "ExIp", laddr)
externalIP = laddr
n.nodeInfo.SetNetSide(true)
//goto SET_ADDR
}
//如果nat,getSelfExternalAddr 无法发现自己的外网地址,则把localaddr 赋值给外网地址
if len(externalIP) == 0 {
externalIP = laddr
}
var externaladdr string
var externalPort int
if cfg.IsSeed {
externalPort = n.listenPort
} else {
exportBytes, err := n.nodeInfo.addrBook.bookDb.Get([]byte(externalPortTag))
if len(exportBytes) != 0 {
externalPort = int(P2pComm.BytesToInt32(exportBytes))
} else {
externalPort = n.listenPort
}
if err != nil {
log.Error("bookDb Get", "nodePort", n.listenPort, "externalPortTag fail err:", err)
}
}
externaladdr = fmt.Sprintf("%v:%v", externalIP, externalPort)
log.Debug("DetectionNodeAddr", "AddBlackList", externaladdr)
n.nodeInfo.blacklist.Add(externaladdr, 0) //把自己的外网地址永久加入到黑名单,以防连接self
if exaddr, err := NewNetAddressString(externaladdr); err == nil {
n.nodeInfo.SetExternalAddr(exaddr)
n.nodeInfo.addrBook.AddOurAddress(exaddr)
} else {
log.Error("DetectionNodeAddr", "error", err.Error())
}
if listaddr, err := NewNetAddressString(fmt.Sprintf("%v:%v", laddr, n.listenPort)); err == nil {
n.nodeInfo.SetListenAddr(listaddr)
n.nodeInfo.addrBook.AddOurAddress(listaddr)
}
break
}
}
func (n *Node) natMapPort() {
n.natNotice()
for {
if n.Size() > 0 {
break
}
time.Sleep(time.Second)
}
var err error
if len(P2pComm.AddrRouteble([]string{n.nodeInfo.GetExternalAddr().String()}, n.nodeInfo.channelVersion)) != 0 { //判断能否连通要映射的端口
log.Info("natMapPort", "addr", "routeble")
p2pcli := NewNormalP2PCli() //检查要映射的IP地址是否已经被映射成功
ok := p2pcli.CheckSelf(n.nodeInfo.GetExternalAddr().String(), n.nodeInfo)
if !ok {
log.Info("natMapPort", "port is used", n.nodeInfo.GetExternalAddr().String())
n.flushNodePort(uint16(n.listenPort), uint16(rand.Intn(64512)+1023))
}
}
_, nodename := n.nodeInfo.addrBook.GetPrivPubKey()
log.Info("natMapPort", "netport", n.nodeInfo.GetExternalAddr().Port)
for i := 0; i < tryMapPortTimes; i++ {
//映射事件持续约48小时
err = nat.Any().AddMapping("TCP", int(n.nodeInfo.GetExternalAddr().Port), n.listenPort, nodename[:8], time.Hour*48)
if err != nil {
if i > tryMapPortTimes/2 { //如果连续失败次数超过最大限制次数的二分之一则切换为随机端口映射
log.Warn("TryNatMapPortFailed", "tryTimes", i, "err", err.Error())
n.flushNodePort(uint16(n.listenPort), uint16(rand.Intn(64512)+1023))
}
log.Info("NatMapPort", "External Port", n.nodeInfo.GetExternalAddr().Port)
continue
}
break
}
if err != nil {
//映射失败
log.Warn("NatMapPort", "Nat", "Faild")
n.flushNodePort(uint16(n.listenPort), uint16(n.listenPort))
n.nodeInfo.natResultChain <- false
return
}
err = n.nodeInfo.addrBook.bookDb.Set([]byte(externalPortTag),
P2pComm.Int32ToBytes(int32(n.nodeInfo.GetExternalAddr().Port))) //把映射成功的端口信息刷入db
if err != nil {
log.Error("NatMapPort", "dbErr", err)
return
}
log.Info("natMapPort", "export insert into db", n.nodeInfo.GetExternalAddr().Port)
n.nodeInfo.natResultChain <- true
refresh := time.NewTimer(mapUpdateInterval)
defer refresh.Stop()
for {
<-refresh.C
log.Info("NatWorkRefresh")
for {
if err := nat.Any().AddMapping("TCP", int(n.nodeInfo.GetExternalAddr().Port), n.listenPort, nodename[:8], time.Hour*48); err != nil {
log.Error("NatMapPort update", "err", err.Error())
time.Sleep(time.Second)
continue
}
break
}
refresh.Reset(mapUpdateInterval)
}
}
func (n *Node) deleteNatMapPort() {
if n.nodeInfo.OutSide() {
return
}
err := nat.Any().DeleteMapping("TCP", int(n.nodeInfo.GetExternalAddr().Port), n.listenPort)
if err != nil {
log.Error("deleteNatMapPort", "DeleteMapping err", err.Error())
}
}
func (n *Node) natNotice() {
<-n.nodeInfo.natNoticeChain
}
func (n *Node) verifyP2PChannel(channel int32) bool {
return channel == n.nodeInfo.cfg.Channel
}
//检测该节点地址是否作为客户端连入, 此时需要维护双向连接, 增加了节点间的连接冗余
func (n *Node) isInBoundPeer(peerName string) (bool, *innerpeer) {
if n.server == nil || n.server.p2pserver == nil {
return false, nil
}
//查询连入的客户端
info := n.server.p2pserver.getInBoundPeerInfo(peerName)
return info != nil, info
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"sync"
"sync/atomic"
"github.com/33cn/chain33/p2p/utils"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
)
// NodeInfo is interface object of the node
type NodeInfo struct {
mtx sync.Mutex
externalAddr *NetAddress
listenAddr *NetAddress
monitorChan chan *Peer
natNoticeChain chan struct{}
natResultChain chan bool
p2pCfg *types.P2P
cfg *subConfig
client queue.Client
blacklist *BlackList
peerInfos *PeerInfos
addrBook *AddrBook // known peers
natDone int32
outSide int32
ServiceType int32
channelVersion int32
}
// NewNodeInfo new a node object
func NewNodeInfo(p2pCfg *types.P2P, subCfg *subConfig) *NodeInfo {
nodeInfo := new(NodeInfo)
nodeInfo.monitorChan = make(chan *Peer, 1024)
nodeInfo.natNoticeChain = make(chan struct{}, 1)
nodeInfo.natResultChain = make(chan bool, 1)
nodeInfo.blacklist = &BlackList{badPeers: make(map[string]int64)}
nodeInfo.p2pCfg = p2pCfg
nodeInfo.cfg = subCfg
nodeInfo.peerInfos = new(PeerInfos)
nodeInfo.peerInfos.infos = make(map[string]*types.Peer)
nodeInfo.externalAddr = new(NetAddress)
nodeInfo.listenAddr = new(NetAddress)
nodeInfo.addrBook = NewAddrBook(p2pCfg, subCfg)
nodeInfo.channelVersion = utils.CalcChannelVersion(subCfg.Channel, VERSION)
return nodeInfo
}
// PeerInfos encapsulation peer information
type PeerInfos struct {
mtx sync.Mutex
//key:peerName
infos map[string]*types.Peer
}
// PeerSize return a size of peer information
func (p *PeerInfos) PeerSize() int {
p.mtx.Lock()
defer p.mtx.Unlock()
return len(p.infos)
}
// FlushPeerInfos flush peer information
func (p *PeerInfos) FlushPeerInfos(in []*types.Peer) {
p.mtx.Lock()
defer p.mtx.Unlock()
for k := range p.infos {
delete(p.infos, k)
}
for _, peer := range in {
p.infos[peer.GetName()] = peer
}
}
// GetPeerInfos return a map for peerinfos
func (p *PeerInfos) GetPeerInfos() map[string]*types.Peer {
p.mtx.Lock()
defer p.mtx.Unlock()
var pinfos = make(map[string]*types.Peer)
for k, v := range p.infos {
pinfos[k] = v
}
return pinfos
}
// SetPeerInfo modify peer infos
func (p *PeerInfos) SetPeerInfo(peer *types.Peer) {
p.mtx.Lock()
defer p.mtx.Unlock()
if peer.GetName() == "" {
return
}
p.infos[peer.GetName()] = peer
}
// GetPeerInfo return a infos by key
func (p *PeerInfos) GetPeerInfo(peerName string) *types.Peer {
p.mtx.Lock()
defer p.mtx.Unlock()
if peer, ok := p.infos[peerName]; ok {
return peer
}
return nil
}
// BlackList badpeers list
type BlackList struct {
mtx sync.Mutex
badPeers map[string]int64
}
// FetchPeerInfo get peerinfo by node
func (nf *NodeInfo) FetchPeerInfo(n *Node) {
var peerlist []*types.Peer
peerInfos := nf.latestPeerInfo(n)
for _, peerinfo := range peerInfos {
peerlist = append(peerlist, peerinfo)
}
nf.flushPeerInfos(peerlist)
}
func (nf *NodeInfo) flushPeerInfos(in []*types.Peer) {
nf.peerInfos.FlushPeerInfos(in)
}
func (nf *NodeInfo) latestPeerInfo(n *Node) map[string]*types.Peer {
var peerlist = make(map[string]*types.Peer)
peers := n.GetRegisterPeers()
log.Debug("latestPeerInfo", "register peer num", len(peers))
for _, peer := range peers {
if !peer.GetRunning() || peer.Addr() == n.nodeInfo.GetExternalAddr().String() {
n.remove(peer.Addr())
continue
}
peerinfo, err := peer.GetPeerInfo()
if err != nil || peerinfo.GetName() == "" {
P2pComm.CollectPeerStat(err, peer)
log.Error("latestPeerInfo", "Err", err, "peer", peer.Addr())
continue
}
var pr types.Peer
pr.Addr = peerinfo.GetAddr()
pr.Port = peerinfo.GetPort()
pr.Name = peerinfo.GetName()
pr.MempoolSize = peerinfo.GetMempoolSize()
pr.Header = peerinfo.GetHeader()
peerlist[pr.Name] = &pr
}
return peerlist
}
// Set modidy nodeinfo by nodeinfo
func (nf *NodeInfo) Set(n *NodeInfo) {
nf.mtx.Lock()
defer nf.mtx.Unlock()
nf = n
}
// Get return nodeinfo
func (nf *NodeInfo) Get() *NodeInfo {
nf.mtx.Lock()
defer nf.mtx.Unlock()
return nf
}
// SetExternalAddr modidy address of the nodeinfo
func (nf *NodeInfo) SetExternalAddr(addr *NetAddress) {
nf.mtx.Lock()
defer nf.mtx.Unlock()
nf.externalAddr = addr
}
// GetExternalAddr return external address
func (nf *NodeInfo) GetExternalAddr() *NetAddress {
nf.mtx.Lock()
defer nf.mtx.Unlock()
return nf.externalAddr
}
// SetListenAddr modify listen address
func (nf *NodeInfo) SetListenAddr(addr *NetAddress) {
nf.mtx.Lock()
defer nf.mtx.Unlock()
nf.listenAddr = addr
}
// GetListenAddr return listen address
func (nf *NodeInfo) GetListenAddr() *NetAddress {
nf.mtx.Lock()
defer nf.mtx.Unlock()
return nf.listenAddr
}
// SetNatDone modify natdone
func (nf *NodeInfo) SetNatDone() {
atomic.StoreInt32(&nf.natDone, 1)
}
// IsNatDone return ture and false
func (nf *NodeInfo) IsNatDone() bool {
return atomic.LoadInt32(&nf.natDone) == 1
}
// IsOutService return true and false for out service
func (nf *NodeInfo) IsOutService() bool {
if !nf.cfg.ServerStart {
return false
}
if nf.OutSide() || nf.ServiceTy() == Service {
return true
}
return false
}
// SetServiceTy set service type
func (nf *NodeInfo) SetServiceTy(ty int32) {
atomic.StoreInt32(&nf.ServiceType, ty)
}
// ServiceTy return serveice type
func (nf *NodeInfo) ServiceTy() int32 {
return atomic.LoadInt32(&nf.ServiceType)
}
// SetNetSide set net side
func (nf *NodeInfo) SetNetSide(ok bool) {
var isoutside int32
if ok {
isoutside = 1
}
atomic.StoreInt32(&nf.outSide, isoutside)
}
// OutSide return true and false for outside
func (nf *NodeInfo) OutSide() bool {
return atomic.LoadInt32(&nf.outSide) == 1
}
// Add add badpeer
func (bl *BlackList) Add(addr string, deadline int64) {
bl.mtx.Lock()
defer bl.mtx.Unlock()
bl.badPeers[addr] = types.Now().Unix() + deadline
}
// Delete delete badpeer
func (bl *BlackList) Delete(addr string) {
bl.mtx.Lock()
defer bl.mtx.Unlock()
delete(bl.badPeers, addr)
}
// Has the badpeer true and false
func (bl *BlackList) Has(addr string) bool {
bl.mtx.Lock()
defer bl.mtx.Unlock()
if _, ok := bl.badPeers[addr]; ok {
return true
}
return false
}
// GetBadPeers reurn black list peers
func (bl *BlackList) GetBadPeers() map[string]int64 {
bl.mtx.Lock()
defer bl.mtx.Unlock()
var copyData = make(map[string]int64)
for k, v := range bl.badPeers {
copyData[k] = v
}
return copyData
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package gossip 实现了gossip网络拓扑
package gossip
import (
"fmt"
"math/rand"
"sync"
"sync/atomic"
"time"
"github.com/33cn/chain33/p2p"
"github.com/33cn/chain33/client"
l "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
_ "google.golang.org/grpc/encoding/gzip" // register gzip
)
// P2PTypeName p2p plugin name for gossip
const P2PTypeName = "gossip"
func init() {
p2p.RegisterP2PCreate(P2PTypeName, New)
}
var (
log = l.New("module", "p2p")
)
// p2p 子配置
type subConfig struct {
// P2P服务监听端口号
Port int32 `protobuf:"varint,1,opt,name=port" json:"port,omitempty"`
// 种子节点,格式为ip:port,多个节点以逗号分隔,如seeds=
Seeds []string `protobuf:"bytes,2,rep,name=seeds" json:"seeds,omitempty"`
// 是否为种子节点
IsSeed bool `protobuf:"varint,3,opt,name=isSeed" json:"isSeed,omitempty"`
//固定连接节点,只连接配置项seeds中的节点
FixedSeed bool `protobuf:"varint,4,opt,name=fixedSeed" json:"fixedSeed,omitempty"`
// 是否使用内置的种子节点
InnerSeedEnable bool `protobuf:"varint,5,opt,name=innerSeedEnable" json:"innerSeedEnable,omitempty"`
// 是否使用Github获取种子节点
UseGithub bool `protobuf:"varint,6,opt,name=useGithub" json:"useGithub,omitempty"`
// 是否作为服务端,对外提供服务
ServerStart bool `protobuf:"varint,7,opt,name=serverStart" json:"serverStart,omitempty"`
// 最多的接入节点个数
InnerBounds int32 `protobuf:"varint,8,opt,name=innerBounds" json:"innerBounds,omitempty"`
//交易开始采用哈希广播的ttl
LightTxTTL int32 `protobuf:"varint,9,opt,name=lightTxTTL" json:"lightTxTTL,omitempty"`
// 最大传播ttl, ttl达到该值将停止继续向外发送
MaxTTL int32 `protobuf:"varint,10,opt,name=maxTTL" json:"maxTTL,omitempty"`
// p2p网络频道,用于区分主网/测试网/其他网络
Channel int32 `protobuf:"varint,11,opt,name=channel" json:"channel,omitempty"`
//区块轻广播的最低打包交易数, 大于该值时区块内交易采用短哈希广播
MinLtBlockTxNum int32 `protobuf:"varint,12,opt,name=minLtBlockTxNum" json:"minLtBlockTxNum,omitempty"`
//指定p2p类型, 支持gossip, dht
}
// P2p interface
type P2p struct {
api client.QueueProtocolAPI
client queue.Client
node *Node
p2pCli EventInterface
txCapcity int32
txFactory chan struct{}
otherFactory chan struct{}
waitRestart chan struct{}
taskGroup *sync.WaitGroup
closed int32
restart int32
p2pCfg *types.P2P
subCfg *subConfig
mgr *p2p.Manager
subChan chan interface{}
}
// New produce a p2p object
func New(mgr *p2p.Manager, subCfg []byte) p2p.IP2P {
cfg := mgr.ChainCfg
p2pCfg := cfg.GetModuleConfig().P2P
mcfg := &subConfig{}
types.MustDecode(subCfg, mcfg)
//主网的channel默认设为0, 测试网未配置时设为默认
if cfg.IsTestNet() && mcfg.Channel == 0 {
mcfg.Channel = defaultTestNetChannel
}
//ttl至少设为2
if mcfg.LightTxTTL <= 1 {
mcfg.LightTxTTL = DefaultLtTxBroadCastTTL
}
if mcfg.MaxTTL <= 0 {
mcfg.MaxTTL = DefaultMaxTxBroadCastTTL
}
if mcfg.MinLtBlockTxNum <= 0 {
mcfg.MinLtBlockTxNum = DefaultMinLtBlockTxNum
}
log.Info("p2p", "Channel", mcfg.Channel, "Version", VERSION, "IsTest", cfg.IsTestNet())
if mcfg.InnerBounds == 0 {
mcfg.InnerBounds = 500
}
log.Info("p2p", "InnerBounds", mcfg.InnerBounds)
node, err := NewNode(mgr, mcfg)
if err != nil {
log.Error(err.Error())
return nil
}
p2p := new(P2p)
p2p.node = node
p2p.p2pCli = NewP2PCli(p2p)
p2p.txFactory = make(chan struct{}, 1000) // 1000 task
p2p.otherFactory = make(chan struct{}, 1000) //other task 1000
p2p.waitRestart = make(chan struct{}, 1)
p2p.txCapcity = 1000
p2p.p2pCfg = p2pCfg
p2p.subCfg = mcfg
p2p.client = mgr.Client
p2p.mgr = mgr
p2p.api = mgr.SysAPI
p2p.taskGroup = &sync.WaitGroup{}
//从p2p manger获取pub的系统消息
p2p.subChan = p2p.mgr.PubSub.Sub(P2PTypeName)
return p2p
}
//Wait wait for ready
func (network *P2p) Wait() {}
func (network *P2p) isClose() bool {
return atomic.LoadInt32(&network.closed) == 1
}
func (network *P2p) isRestart() bool {
return atomic.LoadInt32(&network.restart) == 1
}
// Close network client
func (network *P2p) CloseP2P() {
log.Info("p2p network start shutdown")
atomic.StoreInt32(&network.closed, 1)
//等待业务协程停止
network.waitTaskDone()
network.node.Close()
network.mgr.PubSub.Unsub(network.subChan)
}
// SetQueueClient set the queue
func (network *P2p) StartP2P() {
network.node.SetQueueClient(network.client)
go func(p2p *P2p) {
if p2p.isRestart() {
p2p.node.Start()
atomic.StoreInt32(&p2p.restart, 0)
//开启业务处理协程
network.waitRestart <- struct{}{}
return
}
p2p.subP2pMsg()
key, pub := p2p.node.nodeInfo.addrBook.GetPrivPubKey()
log.Debug("key pub:", pub, "")
if key == "" {
if p2p.p2pCfg.WaitPid { //key为空,则为初始钱包,阻塞模式,一直等到钱包导入助记词,解锁
if p2p.genAirDropKeyFromWallet() != nil {
return
}
} else {
//创建随机Pid,会同时出现node award ,airdropaddr
p2p.node.nodeInfo.addrBook.ResetPeerkey(key, pub)
go p2p.genAirDropKeyFromWallet()
}
} else {
//key 有两种可能,老版本的随机key,也有可能是seed的key, 非阻塞模式
go p2p.genAirDropKeyFromWallet()
}
p2p.node.Start()
log.Debug("SetQueueClient gorountine ret")
}(network)
}
func (network *P2p) loadP2PPrivKeyToWallet() error {
var parm types.ReqWalletImportPrivkey
parm.Privkey, _ = network.node.nodeInfo.addrBook.GetPrivPubKey()
parm.Label = "node award"
ReTry:
resp, err := network.api.ExecWalletFunc("wallet", "WalletImportPrivkey", &parm)
if err != nil {
if err == types.ErrPrivkeyExist {
return nil
}
if err == types.ErrLabelHasUsed {
//切换随机lable
parm.Label = fmt.Sprintf("node award %v", P2pComm.RandStr(3))
time.Sleep(time.Second)
goto ReTry
}
log.Error("loadP2PPrivKeyToWallet", "err", err.Error())
return err
}
log.Debug("loadP2PPrivKeyToWallet", "resp", resp.(*types.WalletAccount))
return nil
}
func (network *P2p) showTaskCapcity() {
ticker := time.NewTicker(time.Second * 5)
log.Info("ShowTaskCapcity", "Capcity", atomic.LoadInt32(&network.txCapcity))
defer ticker.Stop()
for {
if network.isClose() {
log.Debug("ShowTaskCapcity", "loop", "done")
return
}
<-ticker.C
log.Debug("ShowTaskCapcity", "Capcity", atomic.LoadInt32(&network.txCapcity))
}
}
func (network *P2p) genAirDropKeyFromWallet() error {
_, savePub := network.node.nodeInfo.addrBook.GetPrivPubKey()
for {
if network.isClose() {
log.Error("genAirDropKeyFromWallet", "p2p closed", "")
return fmt.Errorf("p2p closed")
}
resp, err := network.api.ExecWalletFunc("wallet", "GetWalletStatus", &types.ReqNil{})
if err != nil {
time.Sleep(time.Second)
continue
}
if resp.(*types.WalletStatus).GetIsWalletLock() { //上锁
if savePub == "" {
log.Warn("P2P Stuck ! Wallet must be unlock and save with mnemonics")
}
time.Sleep(time.Second)
continue
}
if !resp.(*types.WalletStatus).GetIsHasSeed() { //无种子
if savePub == "" {
log.Warn("P2P Stuck ! Wallet must be imported with mnemonics")
}
time.Sleep(time.Second * 5)
continue
}
break
}
r := rand.New(rand.NewSource(types.Now().Unix()))
var minIndex int32 = 100000000
randIndex := minIndex + r.Int31n(1000000)
reqIndex := &types.Int32{Data: randIndex}
msg, err := network.api.ExecWalletFunc("wallet", "NewAccountByIndex", reqIndex)
if err != nil {
log.Error("genAirDropKeyFromWallet", "err", err)
return err
}
var hexPrivkey string
if reply, ok := msg.(*types.ReplyString); !ok {
log.Error("genAirDropKeyFromWallet", "wrong format data", "")
panic(err)
} else {
hexPrivkey = reply.GetData()
}
if hexPrivkey[:2] == "0x" {
hexPrivkey = hexPrivkey[2:]
}
hexPubkey, err := P2pComm.Pubkey(hexPrivkey)
if err != nil {
log.Error("genAirDropKeyFromWallet", "gen pub error", err)
panic(err)
}
log.Info("genAirDropKeyFromWallet", "pubkey", hexPubkey)
if savePub == hexPubkey {
return nil
}
if savePub != "" {
//priv,pub是随机公私钥对,兼容老版本,先对其进行导入钱包处理
err = network.loadP2PPrivKeyToWallet()
if err != nil {
log.Error("genAirDropKeyFromWallet", "loadP2PPrivKeyToWallet error", err)
panic(err)
}
network.node.nodeInfo.addrBook.ResetPeerkey(hexPrivkey, hexPubkey)
//重启p2p模块
log.Info("genAirDropKeyFromWallet", "p2p will Restart....")
network.ReStart()
return nil
}
//覆盖addrbook 中的公私钥对
network.node.nodeInfo.addrBook.ResetPeerkey(hexPrivkey, hexPubkey)
return nil
}
//ReStart p2p
func (network *P2p) ReStart() {
//避免重复
if !atomic.CompareAndSwapInt32(&network.restart, 0, 1) {
return
}
log.Info("p2p restart, wait p2p task done")
network.waitTaskDone()
network.node.Close()
node, err := NewNode(network.mgr, network.subCfg) //创建新的node节点
if err != nil {
panic(err.Error())
}
network.node = node
network.StartP2P()
}
func (network *P2p) subP2pMsg() {
if network.client == nil {
return
}
go network.showTaskCapcity()
go func() {
var taskIndex int64
for data := range network.subChan {
msg, ok := data.(*queue.Message)
if !ok {
log.Debug("subP2pMsg", "assetMsg", ok)
continue
}
if network.isClose() {
log.Debug("subP2pMsg", "loop", "done")
close(network.otherFactory)
close(network.txFactory)
return
}
taskIndex++
log.Debug("p2p recv", "msg", types.GetEventName(int(msg.Ty)), "msg type", msg.Ty, "taskIndex", taskIndex)
if msg.Ty == types.EventTxBroadcast {
network.txFactory <- struct{}{} //allocal task
atomic.AddInt32(&network.txCapcity, -1)
} else {
if msg.Ty != types.EventPeerInfo {
network.otherFactory <- struct{}{}
}
}
switch msg.Ty {
case types.EventTxBroadcast: //广播tx
network.processEvent(msg, taskIndex, network.p2pCli.BroadCastTx)
case types.EventBlockBroadcast: //广播block
network.processEvent(msg, taskIndex, network.p2pCli.BlockBroadcast)
case types.EventFetchBlocks:
network.processEvent(msg, taskIndex, network.p2pCli.GetBlocks)
case types.EventGetMempool:
network.processEvent(msg, taskIndex, network.p2pCli.GetMemPool)
case types.EventPeerInfo:
network.processEvent(msg, taskIndex, network.p2pCli.GetPeerInfo)
case types.EventFetchBlockHeaders:
network.processEvent(msg, taskIndex, network.p2pCli.GetHeaders)
case types.EventGetNetInfo:
network.processEvent(msg, taskIndex, network.p2pCli.GetNetInfo)
default:
log.Warn("unknown msgtype", "msg", msg)
msg.Reply(network.client.NewMessage("", msg.Ty, types.Reply{Msg: []byte("unknown msgtype")}))
<-network.otherFactory
continue
}
}
log.Info("subP2pMsg", "loop", "close")
}()
}
func (network *P2p) processEvent(msg *queue.Message, taskIdx int64, eventFunc p2pEventFunc) {
//检测重启标志,停止分发事件,需要等待重启
if network.isRestart() {
log.Info("wait for p2p restart....")
<-network.waitRestart
log.Info("p2p restart ok....")
}
network.taskGroup.Add(1)
go func() {
defer network.taskGroup.Done()
eventFunc(msg, taskIdx)
}()
}
func (network *P2p) waitTaskDone() {
waitDone := make(chan struct{})
go func() {
defer close(waitDone)
network.taskGroup.Wait()
}()
select {
case <-waitDone:
case <-time.After(time.Second * 20):
log.Error("P2pWaitTaskDone", "err", "20s timeout")
}
}
package gossip
import (
"encoding/hex"
"encoding/json"
"fmt"
"net"
"sort"
"sync/atomic"
"time"
"github.com/33cn/chain33/p2p"
"github.com/33cn/chain33/p2p/utils"
"os"
"strings"
"testing"
"github.com/33cn/chain33/client"
l "github.com/33cn/chain33/common/log"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/wallet"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
testChannel = int32(119)
)
func init() {
l.SetLogLevel("err")
}
func processMsg(q queue.Queue) {
go func() {
cfg := q.GetConfig()
wcli := wallet.New(cfg)
client := q.Client()
wcli.SetQueueClient(client)
//导入种子,解锁钱包
password := "a12345678"
seed := "cushion canal bitter result harvest sentence ability time steel basket useful ask depth sorry area course purpose search exile chapter mountain project ranch buffalo"
saveSeedByPw := &types.SaveSeedByPw{Seed: seed, Passwd: password}
_, err := wcli.GetAPI().ExecWalletFunc("wallet", "SaveSeed", saveSeedByPw)
if err != nil {
return
}
walletUnLock := &types.WalletUnLock{
Passwd: password,
Timeout: 0,
WalletOrTicket: false,
}
_, err = wcli.GetAPI().ExecWalletFunc("wallet", "WalletUnLock", walletUnLock)
if err != nil {
return
}
}()
go func() {
blockchainKey := "blockchain"
client := q.Client()
client.Sub(blockchainKey)
for msg := range client.Recv() {
switch msg.Ty {
case types.EventGetBlocks:
if req, ok := msg.GetData().(*types.ReqBlocks); ok {
if req.Start == 1 {
msg.Reply(client.NewMessage(blockchainKey, types.EventBlocks, &types.Transaction{}))
} else {
msg.Reply(client.NewMessage(blockchainKey, types.EventBlocks, &types.BlockDetails{}))
}
} else {
msg.ReplyErr("Do not support", types.ErrInvalidParam)
}
case types.EventGetHeaders:
if req, ok := msg.GetData().(*types.ReqBlocks); ok {
if req.Start == 10 {
msg.Reply(client.NewMessage(blockchainKey, types.EventHeaders, &types.Transaction{}))
} else {
msg.Reply(client.NewMessage(blockchainKey, types.EventHeaders, &types.Headers{}))
}
} else {
msg.ReplyErr("Do not support", types.ErrInvalidParam)
}
case types.EventGetLastHeader:
msg.Reply(client.NewMessage("p2p", types.EventHeader, &types.Header{Height: 2019}))
case types.EventGetBlockHeight:
msg.Reply(client.NewMessage("p2p", types.EventReplyBlockHeight, &types.ReplyBlockHeight{Height: 2019}))
}
}
}()
go func() {
mempoolKey := "mempool"
client := q.Client()
client.Sub(mempoolKey)
for msg := range client.Recv() {
switch msg.Ty {
case types.EventGetMempoolSize:
msg.Reply(client.NewMessage("p2p", types.EventMempoolSize, &types.MempoolSize{Size: 0}))
}
}
}()
}
//new p2p
func newP2p(cfg *types.Chain33Config, port int32, dbpath string, q queue.Queue) *P2p {
p2pCfg := cfg.GetModuleConfig().P2P
p2pCfg.Enable = true
p2pCfg.DbPath = dbpath
p2pCfg.DbCache = 4
p2pCfg.Driver = "leveldb"
p2pMgr := p2p.NewP2PMgr(cfg)
p2pMgr.Client = q.Client()
p2pMgr.SysAPI, _ = client.New(p2pMgr.Client, nil)
pcfg := &subConfig{}
types.MustDecode(cfg.GetSubConfig().P2P[P2PTypeName], pcfg)
pcfg.Port = port
pcfg.Channel = testChannel
pcfg.ServerStart = true
pcfg.MinLtBlockTxNum = 1
subCfgBytes, _ := json.Marshal(pcfg)
p2pcli := New(p2pMgr, subCfgBytes).(*P2p)
p2pcli.node.nodeInfo.addrBook.initKey()
privkey, _ := p2pcli.node.nodeInfo.addrBook.GetPrivPubKey()
p2pcli.node.nodeInfo.addrBook.bookDb.Set([]byte(privKeyTag), []byte(privkey))
p2pcli.node.nodeInfo.SetServiceTy(7)
p2pcli.StartP2P()
return p2pcli
}
//free P2p
func freeP2p(p2p *P2p) {
p2p.CloseP2P()
if err := os.RemoveAll(p2p.p2pCfg.DbPath); err != nil {
log.Error("removeTestDbErr", "err", err)
}
}
func testP2PEvent(t *testing.T, p2p *P2p) {
msgs := make([]*queue.Message, 0)
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventBlockBroadcast, &types.Block{}))
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventTxBroadcast, &types.Transaction{}))
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventFetchBlocks, &types.ReqBlocks{}))
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventGetMempool, nil))
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventPeerInfo, nil))
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventGetNetInfo, nil))
msgs = append(msgs, p2p.client.NewMessage("p2p", types.EventFetchBlockHeaders, &types.ReqBlocks{}))
for _, msg := range msgs {
p2p.mgr.PubSub.Pub(msg, P2PTypeName)
}
}
func testNetInfo(t *testing.T, p2p *P2p) {
p2p.node.nodeInfo.IsNatDone()
p2p.node.nodeInfo.SetNatDone()
p2p.node.nodeInfo.Get()
p2p.node.nodeInfo.Set(p2p.node.nodeInfo)
assert.NotNil(t, p2p.node.nodeInfo.GetListenAddr())
assert.NotNil(t, p2p.node.nodeInfo.GetExternalAddr())
}
//测试Peer
func testPeer(t *testing.T, p2p *P2p, q queue.Queue) {
cfg := types.NewChain33Config(types.ReadFile("../../../chain33.toml"))
conn, err := grpc.Dial("localhost:53802", grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")))
assert.Nil(t, err)
defer conn.Close()
remote, err := NewNetAddressString(fmt.Sprintf("127.0.0.1:%d", p2p.node.listenPort))
assert.Nil(t, err)
localP2P := newP2p(cfg, 43802, "testPeer", q)
defer freeP2p(localP2P)
t.Log(localP2P.node.CacheBoundsSize())
t.Log(localP2P.node.GetCacheBounds())
localP2P.node.RemoveCachePeer("localhost:12345")
assert.False(t, localP2P.node.HasCacheBound("localhost:12345"))
peer, err := P2pComm.dialPeer(remote, localP2P.node)
assert.Nil(t, err)
defer peer.Close()
peer.MakePersistent()
localP2P.node.addPeer(peer)
_, localPeerName := localP2P.node.nodeInfo.addrBook.GetPrivPubKey()
var info *innerpeer
t.Log("WaitRegisterPeerStart...")
trytime := 0
for info == nil || info.p2pversion == 0 {
trytime++
time.Sleep(time.Millisecond * 100)
info = p2p.node.server.p2pserver.getInBoundPeerInfo(localPeerName)
if trytime > 100 {
return
}
}
exist, _ := p2p.node.isInBoundPeer(localPeerName)
assert.True(t, exist)
t.Log("WaitRegisterPeerStop...")
p2pcli := NewNormalP2PCli()
num, err := p2pcli.GetInPeersNum(peer)
assert.Equal(t, 1, num)
assert.Nil(t, err)
tx1 := &types.Transaction{Execer: []byte("testTx1")}
tx2 := &types.Transaction{Execer: []byte("testTx2")}
localP2P.node.pubToPeer(&types.P2PTx{Tx: tx1}, peer.GetPeerName())
p2p.node.server.p2pserver.pubToStream(&types.P2PTx{Tx: tx2}, info.name)
t.Log("WaitRegisterTxFilterStart...")
for !(txHashFilter.Contains(hex.EncodeToString(tx1.Hash())) &&
txHashFilter.Contains(hex.EncodeToString(tx1.Hash()))) {
time.Sleep(time.Millisecond * 10)
}
t.Log("WaitRegisterTxFilterStop")
localP2P.node.AddCachePeer(peer)
assert.Equal(t, localP2P.node.CacheBoundsSize(), len(localP2P.node.GetCacheBounds()))
peer.GetRunning()
localP2P.node.nodeInfo.FetchPeerInfo(localP2P.node)
peers, infos := localP2P.node.GetActivePeers()
assert.Equal(t, len(peers), len(infos))
localP2P.node.flushNodePort(43803, 43802)
localP2P.node.nodeInfo.peerInfos.SetPeerInfo(nil)
localP2P.node.nodeInfo.peerInfos.GetPeerInfo("1222")
t.Log(p2p.node.GetRegisterPeer("localhost:43802"))
//测试发送Ping消息
err = p2pcli.SendPing(peer, localP2P.node.nodeInfo)
assert.Nil(t, err)
//获取peer节点的被连接数
pnum, err := p2pcli.GetInPeersNum(peer)
assert.Nil(t, err)
assert.Equal(t, 1, pnum)
_, err = peer.GetPeerInfo()
assert.Nil(t, err)
//获取节点列表
_, err = p2pcli.GetAddrList(peer)
assert.Nil(t, err)
_, err = p2pcli.SendVersion(peer, localP2P.node.nodeInfo)
assert.Nil(t, err)
t.Log(p2pcli.CheckPeerNatOk("localhost:53802", localP2P.node.nodeInfo))
t.Log("checkself:", p2pcli.CheckSelf("loadhost:43803", localP2P.node.nodeInfo))
_, err = p2pcli.GetAddr(peer)
assert.Nil(t, err)
localP2P.node.pubsub.FIFOPub(&types.P2PTx{Tx: &types.Transaction{}, Route: &types.P2PRoute{}}, "tx")
localP2P.node.pubsub.FIFOPub(&types.P2PBlock{Block: &types.Block{}}, "block")
// //测试获取高度
height, err := p2pcli.GetBlockHeight(localP2P.node.nodeInfo)
assert.Nil(t, err)
assert.Equal(t, int(height), 2019)
assert.Equal(t, false, p2pcli.CheckSelf("localhost:53802", localP2P.node.nodeInfo))
//测试下载
job := NewDownloadJob(NewP2PCli(localP2P).(*Cli), []*Peer{peer})
job.GetFreePeer(1)
var ins []*types.Inventory
var inv types.Inventory
inv.Ty = msgBlock
inv.Height = 2
ins = append(ins, &inv)
var bChan = make(chan *types.BlockPid, 256)
job.syncDownloadBlock(peer, ins[0], bChan)
respIns := job.DownloadBlock(ins, bChan)
t.Log(respIns)
job.ResetDownloadPeers([]*Peer{peer})
t.Log(job.avalidPeersNum())
job.setBusyPeer(peer.GetPeerName())
job.setFreePeer(peer.GetPeerName())
job.removePeer(peer.GetPeerName())
job.CancelJob()
localP2P.node.addPeer(peer)
assert.True(t, localP2P.node.needMore())
peer.Close()
localP2P.node.remove(peer.peerAddr.String())
}
//测试grpc 多连接
func testGrpcConns(t *testing.T) {
var conns []*grpc.ClientConn
for i := 0; i < maxSamIPNum; i++ {
conn, err := grpc.Dial("localhost:53802", grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")))
assert.Nil(t, err)
cli := types.NewP2PgserviceClient(conn)
_, err = cli.GetHeaders(context.Background(), &types.P2PGetHeaders{
StartHeight: 0, EndHeight: 0, Version: 1002}, grpc.FailFast(true))
assert.Equal(t, false, strings.Contains(err.Error(), "no authorized"))
conns = append(conns, conn)
}
conn, err := grpc.Dial("localhost:53802", grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")))
assert.Nil(t, err)
cli := types.NewP2PgserviceClient(conn)
_, err = cli.GetHeaders(context.Background(), &types.P2PGetHeaders{
StartHeight: 0, EndHeight: 0, Version: 1002}, grpc.FailFast(true))
assert.Equal(t, true, strings.Contains(err.Error(), "no authorized"))
conn.Close()
for _, conn := range conns {
conn.Close()
}
}
//测试grpc 流多连接
func testGrpcStreamConns(t *testing.T, p2p *P2p) {
conn, err := grpc.Dial("localhost:53802", grpc.WithInsecure(),
grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")))
assert.Nil(t, err)
cli := types.NewP2PgserviceClient(conn)
var p2pdata types.P2PGetData
resp, err := cli.GetData(context.Background(), &p2pdata)
assert.Nil(t, err)
_, err = resp.Recv()
assert.Equal(t, true, strings.Contains(err.Error(), "no authorized"))
ping, err := P2pComm.NewPingData(p2p.node.nodeInfo)
assert.Nil(t, err)
_, err = cli.ServerStreamSend(context.Background(), ping)
assert.Nil(t, err)
_, err = cli.ServerStreamRead(context.Background())
assert.Nil(t, err)
var emptyBlock types.P2PBlock
_, err = cli.BroadCastBlock(context.Background(), &emptyBlock)
assert.Equal(t, true, strings.Contains(err.Error(), "no authorized"))
conn.Close()
}
func testP2pComm(t *testing.T, p2p *P2p) {
addrs := P2pComm.AddrRouteble([]string{"localhost:53802"}, utils.CalcChannelVersion(testChannel, VERSION))
t.Log(addrs)
i32 := P2pComm.BytesToInt32([]byte{0xff})
t.Log(i32)
_, _, err := P2pComm.GenPrivPubkey()
assert.Nil(t, err)
ping, err := P2pComm.NewPingData(p2p.node.nodeInfo)
assert.Nil(t, err)
assert.Equal(t, true, P2pComm.CheckSign(ping))
assert.IsType(t, "string", P2pComm.GetLocalAddr())
assert.Equal(t, 5, len(P2pComm.RandStr(5)))
}
func testAddrBook(t *testing.T, p2p *P2p) {
prv, pub, err := P2pComm.GenPrivPubkey()
if err != nil {
t.Log(err.Error())
return
}
t.Log("priv:", hex.EncodeToString(prv), "pub:", hex.EncodeToString(pub))
pubstr, err := P2pComm.Pubkey(hex.EncodeToString(prv))
if err != nil {
t.Log(err.Error())
return
}
t.Log("GenPubkey:", pubstr)
addrBook := p2p.node.nodeInfo.addrBook
addrBook.AddOurAddress(NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 1234))
addrBook.AddAddress(nil, nil)
addrBook.AddAddress(NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 1234), nil)
addrBook.AddAddress(NewNetAddressIPPort(net.ParseIP("127.0.0.2"), 1234), nil)
assert.True(t, addrBook.ISOurAddress(NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 1234)))
assert.True(t, addrBook.IsOurStringAddress("127.0.0.1:1234"))
assert.Equal(t, addrBook.Size(), len(addrBook.GetPeers()))
addrBook.setAddrStat("127.0.0.2:1234", true)
addrBook.setAddrStat("127.0.0.2:1234", false)
addrBook.saveToDb()
addrBook.GetPeerStat("locolhost:43802")
addrBook.genPubkey(hex.EncodeToString(prv))
assert.Equal(t, addrBook.genPubkey(hex.EncodeToString(prv)), pubstr)
addrBook.Save()
addrBook.GetPeers()
addrBook.GetAddrs()
addrBook.ResetPeerkey("", "")
privkey, _ := addrBook.GetPrivPubKey()
assert.NotEmpty(t, privkey)
addrBook.ResetPeerkey(hex.EncodeToString(prv), pubstr)
resetkey, _ := addrBook.GetPrivPubKey()
assert.NotEqual(t, resetkey, privkey)
}
func testRestart(t *testing.T, p2p *P2p) {
client := p2p.client
assert.False(t, p2p.isRestart())
p2p.txFactory <- struct{}{}
p2p.processEvent(client.NewMessage("p2p", types.EventTxBroadcast, &types.Transaction{}), 128, p2p.p2pCli.BroadCastTx)
atomic.StoreInt32(&p2p.restart, 1)
p2p.ReStart()
atomic.StoreInt32(&p2p.restart, 0)
p2p.ReStart()
}
func Test_p2p(t *testing.T) {
cfg := types.NewChain33Config(types.ReadFile("../../../chain33.toml"))
q := queue.New("channel")
q.SetConfig(cfg)
go q.Start()
processMsg(q)
p2p := newP2p(cfg, 53802, "testP2p", q)
p2p.Wait()
defer freeP2p(p2p)
defer q.Close()
testP2PEvent(t, p2p)
testNetInfo(t, p2p)
testPeer(t, p2p, q)
testGrpcConns(t)
testGrpcStreamConns(t, p2p)
testP2pComm(t, p2p)
testAddrBook(t, p2p)
testRestart(t, p2p)
}
func Test_AddDelStream(t *testing.T) {
s := NewP2pServer()
peerName := "testpeer"
delChan := s.addStreamHandler(peerName)
//replace
dataChan := s.addStreamHandler(peerName)
_, ok := <-delChan
assert.False(t, ok)
//del old
s.deleteStream(peerName, delChan)
_, ok = s.streams[peerName]
assert.True(t, ok)
//del nil
s.deleteStream("", delChan)
//del exist
s.deleteStream(peerName, dataChan)
_, ok = s.streams[peerName]
assert.False(t, ok)
}
func TestRandStr(t *testing.T) {
t.Log(P2pComm.RandStr(5))
}
func TestBytesToInt32(t *testing.T) {
t.Log(P2pComm.BytesToInt32([]byte{0xff}))
t.Log(P2pComm.Int32ToBytes(255))
}
func TestSortArr(t *testing.T) {
var Inventorys = make(Invs, 0)
for i := 100; i >= 0; i-- {
var inv types.Inventory
inv.Ty = 111
inv.Height = int64(i)
Inventorys = append(Inventorys, &inv)
}
sort.Sort(Inventorys)
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"encoding/hex"
"fmt"
"io"
"math/rand"
"net"
"github.com/33cn/chain33/p2p/utils"
"sync/atomic"
"time"
"github.com/33cn/chain33/queue"
pb "github.com/33cn/chain33/types"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
type p2pEventFunc func(message *queue.Message, taskIndex int64)
// EventInterface p2p subscribe to the event hander interface
type EventInterface interface {
BroadCastTx(msg *queue.Message, taskindex int64)
GetMemPool(msg *queue.Message, taskindex int64)
GetPeerInfo(msg *queue.Message, taskindex int64)
GetHeaders(msg *queue.Message, taskindex int64)
GetBlocks(msg *queue.Message, taskindex int64)
BlockBroadcast(msg *queue.Message, taskindex int64)
GetNetInfo(msg *queue.Message, taskindex int64)
}
// NormalInterface subscribe to the event hander interface
type NormalInterface interface {
GetAddr(peer *Peer) ([]string, error)
SendVersion(peer *Peer, nodeinfo *NodeInfo) (string, error)
SendPing(peer *Peer, nodeinfo *NodeInfo) error
GetBlockHeight(nodeinfo *NodeInfo) (int64, error)
CheckPeerNatOk(addr string, nodeInfo *NodeInfo) bool
GetAddrList(peer *Peer) (map[string]int64, error)
GetInPeersNum(peer *Peer) (int, error)
CheckSelf(addr string, nodeinfo *NodeInfo) bool
}
// Cli p2p client
type Cli struct {
network *P2p
}
// NewP2PCli produce a p2p client
func NewP2PCli(network *P2p) EventInterface {
if network == nil {
return nil
}
pcli := &Cli{
network: network,
}
return pcli
}
// NewNormalP2PCli produce a normal client
func NewNormalP2PCli() NormalInterface {
return &Cli{}
}
// BroadCastTx broadcast transactions
func (m *Cli) BroadCastTx(msg *queue.Message, taskindex int64) {
defer func() {
<-m.network.txFactory
atomic.AddInt32(&m.network.txCapcity, 1)
log.Debug("BroadCastTx", "task complete:", taskindex)
}()
if tx, ok := msg.GetData().(*pb.Transaction); ok {
txHash := hex.EncodeToString(tx.Hash())
//此处使用新分配结构,避免重复修改已保存的ttl
route := &pb.P2PRoute{TTL: 1}
//是否已存在记录,不存在表示本节点发起的交易
data, exist := txHashFilter.Get(txHash)
if ttl, ok := data.(*pb.P2PRoute); exist && ok {
route.TTL = ttl.GetTTL() + 1
} else {
txHashFilter.Add(txHash, true)
}
m.network.node.pubsub.FIFOPub(&pb.P2PTx{Tx: tx, Route: route}, "tx")
msg.Reply(m.network.client.NewMessage("mempool", pb.EventReply, pb.Reply{IsOk: true, Msg: []byte("ok")}))
}
}
// GetMemPool get mempool contents
func (m *Cli) GetMemPool(msg *queue.Message, taskindex int64) {
defer func() {
<-m.network.otherFactory
log.Debug("GetMemPool", "task complete:", taskindex)
}()
var Txs = make([]*pb.Transaction, 0)
var ableInv = make([]*pb.Inventory, 0)
peers, _ := m.network.node.GetActivePeers()
for _, peer := range peers {
//获取远程 peer invs
resp, err := peer.mconn.gcli.GetMemPool(context.Background(),
&pb.P2PGetMempool{Version: m.network.node.nodeInfo.channelVersion}, grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
if err == pb.ErrVersion {
peer.version.SetSupport(false)
P2pComm.CollectPeerStat(err, peer)
}
continue
}
invs := resp.GetInvs()
//与本地mempool 对比 tx数组
tmpMsg := m.network.client.NewMessage("mempool", pb.EventGetMempool, nil)
txresp, err := m.network.client.Wait(tmpMsg)
if err != nil {
continue
}
txlist := txresp.GetData().(*pb.ReplyTxList)
txs := txlist.GetTxs()
var txmap = make(map[string]*pb.Transaction)
for _, tx := range txs {
txmap[hex.EncodeToString(tx.Hash())] = tx
}
//去重过滤
for _, inv := range invs {
if _, ok := txmap[hex.EncodeToString(inv.Hash)]; !ok {
ableInv = append(ableInv, inv)
}
}
//获取真正的交易Tx call GetData
datacli, dataerr := peer.mconn.gcli.GetData(context.Background(),
&pb.P2PGetData{Invs: ableInv, Version: m.network.node.nodeInfo.channelVersion}, grpc.FailFast(true))
P2pComm.CollectPeerStat(dataerr, peer)
if dataerr != nil {
continue
}
invdatas, recerr := datacli.Recv()
if recerr != nil && recerr != io.EOF {
log.Error("GetMemPool", "err", recerr.Error())
err = datacli.CloseSend()
if err != nil {
log.Error("datacli", "close err", err)
}
continue
}
for _, invdata := range invdatas.Items {
Txs = append(Txs, invdata.GetTx())
}
err = datacli.CloseSend()
if err != nil {
log.Error("datacli", "CloseSend err", err)
}
break
}
msg.Reply(m.network.client.NewMessage("mempool", pb.EventReplyTxList, &pb.ReplyTxList{Txs: Txs}))
}
// GetAddr get address list
func (m *Cli) GetAddr(peer *Peer) ([]string, error) {
resp, err := peer.mconn.gcli.GetAddr(context.Background(), &pb.P2PGetAddr{Nonce: int64(rand.Int31n(102040))},
grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
return nil, err
}
log.Debug("GetAddr Resp", "Resp", resp, "addrlist", resp.Addrlist)
return resp.Addrlist, nil
}
// GetInPeersNum return normal number of peers
func (m *Cli) GetInPeersNum(peer *Peer) (int, error) {
ping, err := P2pComm.NewPingData(peer.node.nodeInfo)
if err != nil {
return 0, err
}
resp, err := peer.mconn.gcli.CollectInPeers(context.Background(), ping,
grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
return 0, err
}
return len(resp.GetPeers()), nil
}
// GetAddrList return a map for address-prot
func (m *Cli) GetAddrList(peer *Peer) (map[string]int64, error) {
var addrlist = make(map[string]int64)
if peer == nil {
return addrlist, fmt.Errorf("pointer is nil")
}
resp, err := peer.mconn.gcli.GetAddrList(context.Background(), &pb.P2PGetAddr{Nonce: int64(rand.Int31n(102040))},
grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
return addrlist, err
}
//获取本地高度
client := peer.node.nodeInfo.client
msg := client.NewMessage("blockchain", pb.EventGetLastHeader, nil)
err = client.SendTimeout(msg, true, time.Second*10)
if err != nil {
log.Error("getLocalPeerInfo blockchain", "Error", err.Error())
return addrlist, err
}
respmsg, err := client.WaitTimeout(msg, time.Second*30)
if err != nil {
return addrlist, err
}
localBlockHeight := respmsg.GetData().(*pb.Header).GetHeight()
peerinfos := resp.GetPeerinfo()
for _, peerinfo := range peerinfos {
if localBlockHeight-peerinfo.GetHeader().GetHeight() < 2048 {
addrlist[fmt.Sprintf("%v:%v", peerinfo.GetAddr(), peerinfo.GetPort())] = peerinfo.GetHeader().GetHeight()
}
}
return addrlist, nil
}
// SendVersion send version
func (m *Cli) SendVersion(peer *Peer, nodeinfo *NodeInfo) (string, error) {
client := nodeinfo.client
msg := client.NewMessage("blockchain", pb.EventGetBlockHeight, nil)
err := client.SendTimeout(msg, true, time.Second*10)
if err != nil {
log.Error("SendVesion", "Error", err.Error())
return "", err
}
rsp, err := client.WaitTimeout(msg, time.Second*20)
if err != nil {
log.Error("GetHeight", "Error", err.Error())
return "", err
}
blockheight := rsp.GetData().(*pb.ReplyBlockHeight).GetHeight()
randNonce := rand.Int31n(102040)
p2pPrivKey, _ := nodeinfo.addrBook.GetPrivPubKey()
in, err := P2pComm.Signature(p2pPrivKey,
&pb.P2PPing{Nonce: int64(randNonce), Addr: nodeinfo.GetExternalAddr().IP.String(),
Port: int32(nodeinfo.GetExternalAddr().Port)})
if err != nil {
log.Error("Signature", "Error", err.Error())
return "", err
}
addrfrom := nodeinfo.GetExternalAddr().String()
resp, err := peer.mconn.gcli.Version2(context.Background(), &pb.P2PVersion{Version: nodeinfo.channelVersion, Service: int64(nodeinfo.ServiceTy()), Timestamp: pb.Now().Unix(),
AddrRecv: peer.Addr(), AddrFrom: addrfrom, Nonce: int64(rand.Int31n(102040)),
UserAgent: hex.EncodeToString(in.Sign.GetPubkey()), StartHeight: blockheight}, grpc.FailFast(true))
log.Debug("SendVersion", "resp", resp, "addrfrom", addrfrom, "sendto", peer.Addr())
if err != nil {
log.Error("SendVersion", "Verson", err.Error(), "peer", peer.Addr())
if err == pb.ErrVersion {
peer.version.SetSupport(false)
P2pComm.CollectPeerStat(err, peer)
}
return "", err
}
P2pComm.CollectPeerStat(err, peer)
log.Debug("SHOW VERSION BACK", "VersionBack", resp, "peer", peer.Addr())
_, ver := utils.DecodeChannelVersion(resp.GetVersion())
peer.version.SetVersion(ver)
ip, _, err := net.SplitHostPort(resp.GetAddrRecv())
if err == nil {
if ip != nodeinfo.GetExternalAddr().IP.String() {
log.Debug("sendVersion", "externalip", ip)
if peer.IsPersistent() {
//永久加入黑名单
nodeinfo.blacklist.Add(ip, 0)
}
}
}
if exaddr, err := NewNetAddressString(resp.GetAddrRecv()); err == nil {
nodeinfo.SetExternalAddr(exaddr)
}
return resp.GetUserAgent(), nil
}
// SendPing send ping
func (m *Cli) SendPing(peer *Peer, nodeinfo *NodeInfo) error {
randNonce := rand.Int31n(102040)
ping := &pb.P2PPing{Nonce: int64(randNonce), Addr: nodeinfo.GetExternalAddr().IP.String(), Port: int32(nodeinfo.GetExternalAddr().Port)}
p2pPrivKey, _ := nodeinfo.addrBook.GetPrivPubKey()
_, err := P2pComm.Signature(p2pPrivKey, ping)
if err != nil {
log.Error("Signature", "Error", err.Error())
return err
}
r, err := peer.mconn.gcli.Ping(context.Background(), ping, grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
return err
}
log.Debug("SendPing", "Peer", peer.Addr(), "nonce", randNonce, "recv", r.Nonce)
return nil
}
// GetBlockHeight return block height
func (m *Cli) GetBlockHeight(nodeinfo *NodeInfo) (int64, error) {
client := nodeinfo.client
msg := client.NewMessage("blockchain", pb.EventGetLastHeader, nil)
err := client.SendTimeout(msg, true, time.Minute)
if err != nil {
log.Error("GetBlockHeight", "Error", err.Error())
return 0, err
}
resp, err := client.WaitTimeout(msg, time.Minute)
if err != nil {
return 0, err
}
header := resp.GetData().(*pb.Header)
return header.GetHeight(), nil
}
// GetPeerInfo return peer information
func (m *Cli) GetPeerInfo(msg *queue.Message, taskindex int64) {
defer func() {
log.Debug("GetPeerInfo", "task complete:", taskindex)
}()
peerinfo, err := m.getLocalPeerInfo()
if err != nil {
log.Error("GetPeerInfo", "p2p cli Err", err.Error())
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventPeerList, &pb.PeerList{Peers: m.peerInfos()}))
return
}
var peers = m.peerInfos()
var peer pb.Peer
peer.Addr = peerinfo.GetAddr()
peer.Port = peerinfo.GetPort()
peer.Name = peerinfo.GetName()
peer.MempoolSize = peerinfo.GetMempoolSize()
peer.Self = true
peer.Header = peerinfo.GetHeader()
peers = append(peers, &peer)
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventPeerList, &pb.PeerList{Peers: peers}))
}
// GetHeaders get headers information
func (m *Cli) GetHeaders(msg *queue.Message, taskindex int64) {
defer func() {
<-m.network.otherFactory
log.Debug("GetHeaders", "task complete:", taskindex)
}()
if m.network.node.Size() == 0 {
log.Debug("GetHeaders", "boundNum", 0)
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventReply, pb.Reply{Msg: []byte("no peers")}))
return
}
req := msg.GetData().(*pb.ReqBlocks)
pid := req.GetPid()
if len(pid) == 0 {
log.Debug("GetHeaders:pid is nil")
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventReply, pb.Reply{Msg: []byte("no pid")}))
return
}
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventReply, pb.Reply{IsOk: true, Msg: []byte("ok")}))
peers, infos := m.network.node.GetActivePeers()
if peer, ok := peers[pid[0]]; ok && peer != nil {
var err error
headers, err := peer.mconn.gcli.GetHeaders(context.Background(), &pb.P2PGetHeaders{StartHeight: req.GetStart(), EndHeight: req.GetEnd(),
Version: m.network.node.nodeInfo.channelVersion}, grpc.FailFast(true))
P2pComm.CollectPeerStat(err, peer)
if err != nil {
log.Error("GetBlocks", "Err", err.Error())
if err == pb.ErrVersion {
peer.version.SetSupport(false)
P2pComm.CollectPeerStat(err, peer) //把no support 消息传递过去
}
return
}
client := m.network.node.nodeInfo.client
msg := client.NewMessage("blockchain", pb.EventAddBlockHeaders, &pb.HeadersPid{Pid: pid[0], Headers: &pb.Headers{Items: headers.GetHeaders()}})
err = client.Send(msg, false)
if err != nil {
log.Error("send", "to blockchain EventAddBlockHeaders msg Err", err.Error())
}
} else {
//当请求的pid不是ActivePeer时需要打印日志方便问题定位
log.Debug("GetHeaders", "pid", pid[0], "ActivePeers", peers, "infos", infos)
}
}
// GetBlocks get blocks information
func (m *Cli) GetBlocks(msg *queue.Message, taskindex int64) {
defer func() {
<-m.network.otherFactory
log.Debug("GetBlocks", "task complete:", taskindex)
}()
if m.network.node.Size() == 0 {
log.Debug("GetBlocks", "boundNum", 0)
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventReply, pb.Reply{Msg: []byte("no peers")}))
return
}
req := msg.GetData().(*pb.ReqBlocks)
log.Debug("GetBlocks", "start", req.GetStart(), "end", req.GetEnd())
pids := req.GetPid()
log.Debug("GetBlocks", "pids", pids)
var Inventorys = make([]*pb.Inventory, 0)
for i := req.GetStart(); i <= req.GetEnd(); i++ {
var inv pb.Inventory
inv.Ty = msgBlock
inv.Height = i
Inventorys = append(Inventorys, &inv)
}
MaxInvs := &pb.P2PInv{Invs: Inventorys}
var downloadPeers []*Peer
peers, infos := m.network.node.GetActivePeers()
if len(pids) > 0 && pids[0] != "" { //指定Pid 下载数据
log.Debug("fetch from peer in pids", "pids", pids)
for _, pid := range pids {
if peer, ok := peers[pid]; ok && peer != nil {
downloadPeers = append(downloadPeers, peer)
}
}
} else {
log.Debug("fetch from all peers in pids")
for name, peer := range peers {
info, ok := infos[name]
if !ok || info.GetHeader().GetHeight() < req.GetStart() { //高度不符合要求
continue
}
downloadPeers = append(downloadPeers, peer)
}
}
if len(downloadPeers) == 0 {
log.Error("GetBlocks", "downloadPeers", 0, "peers", peers, "infos", infos)
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventReply, pb.Reply{Msg: []byte("no downloadPeers")}))
return
}
msg.Reply(m.network.client.NewMessage("blockchain", pb.EventReply, pb.Reply{IsOk: true, Msg: []byte("downloading...")}))
//使用新的下载模式进行下载
var bChan = make(chan *pb.BlockPid, 512)
invs := MaxInvs.GetInvs()
job := NewDownloadJob(m, downloadPeers)
var jobcancel int32
go func(cancel *int32, invs []*pb.Inventory) {
for {
if atomic.LoadInt32(cancel) == 1 {
return
}
invs = job.DownloadBlock(invs, bChan)
if len(invs) == 0 {
return
}
if job.avalidPeersNum() <= 0 {
job.ResetDownloadPeers(downloadPeers)
continue
}
if job.isCancel() {
return
}
}
}(&jobcancel, invs)
i := 0
for {
if job.isCancel() {
return
}
timeout := time.NewTimer(time.Minute * 10)
select {
case <-timeout.C:
atomic.StoreInt32(&jobcancel, 1)
job.CancelJob()
log.Error("download timeout")
return
case blockpid := <-bChan:
newmsg := m.network.node.nodeInfo.client.NewMessage("blockchain", pb.EventSyncBlock, blockpid)
err := m.network.node.nodeInfo.client.SendTimeout(newmsg, false, 60*time.Second)
if err != nil {
log.Error("send", "to blockchain EventSyncBlock msg err", err)
}
i++
if i == len(MaxInvs.GetInvs()) {
return
}
}
if !timeout.Stop() {
<-timeout.C
}
}
}
// BlockBroadcast block broadcast
func (m *Cli) BlockBroadcast(msg *queue.Message, taskindex int64) {
defer func() {
<-m.network.otherFactory
log.Debug("BlockBroadcast", "task complete:", taskindex)
}()
if block, ok := msg.GetData().(*pb.Block); ok {
pb.AssertConfig(m.network.client)
blockHashFilter.Add(hex.EncodeToString(block.Hash(m.network.client.GetConfig())), true)
m.network.node.pubsub.FIFOPub(&pb.P2PBlock{Block: block}, "block")
}
}
// GetNetInfo get network information
func (m *Cli) GetNetInfo(msg *queue.Message, taskindex int64) {
defer func() {
<-m.network.otherFactory
log.Debug("GetNetInfo", "task complete:", taskindex)
}()
var netinfo pb.NodeNetInfo
netinfo.Externaladdr = m.network.node.nodeInfo.GetExternalAddr().String()
netinfo.Localaddr = m.network.node.nodeInfo.GetListenAddr().String()
netinfo.Service = m.network.node.nodeInfo.IsOutService()
netinfo.Outbounds = int32(m.network.node.Size())
netinfo.Inbounds = int32(len(m.network.node.server.p2pserver.getInBoundPeers()))
msg.Reply(m.network.client.NewMessage("rpc", pb.EventReplyNetInfo, &netinfo))
}
// CheckPeerNatOk check peer is ok or not
func (m *Cli) CheckPeerNatOk(addr string, info *NodeInfo) bool {
//连接自己的地址信息做测试
return !(len(P2pComm.AddrRouteble([]string{addr}, info.channelVersion)) == 0)
}
// CheckSelf check addrbook privPubKey
func (m *Cli) CheckSelf(addr string, nodeinfo *NodeInfo) bool {
netaddr, err := NewNetAddressString(addr)
if err != nil {
log.Error("AddrRouteble", "NewNetAddressString", err.Error())
return false
}
conn, err := netaddr.DialTimeout(nodeinfo.channelVersion)
if err != nil {
return false
}
defer conn.Close()
cli := pb.NewP2PgserviceClient(conn)
resp, err := cli.GetPeerInfo(context.Background(),
&pb.P2PGetPeerInfo{Version: nodeinfo.channelVersion}, grpc.FailFast(true))
if err != nil {
return false
}
_, selfName := nodeinfo.addrBook.GetPrivPubKey()
return resp.GetName() == selfName
}
func (m *Cli) peerInfos() []*pb.Peer {
peerinfos := m.network.node.nodeInfo.peerInfos.GetPeerInfos()
var peers []*pb.Peer
for _, peer := range peerinfos {
if peer.GetAddr() == m.network.node.nodeInfo.GetExternalAddr().IP.String() && peer.GetPort() == int32(m.network.node.nodeInfo.GetExternalAddr().Port) {
continue
}
peers = append(peers, peer)
}
return peers
}
func (m *Cli) getLocalPeerInfo() (*pb.P2PPeerInfo, error) {
client := m.network.client
msg := client.NewMessage("mempool", pb.EventGetMempoolSize, nil)
err := client.SendTimeout(msg, true, time.Second*10) //发送超时
if err != nil {
log.Error("GetPeerInfo mempool", "Error", err.Error())
return nil, err
}
resp, err := client.WaitTimeout(msg, time.Second*30)
if err != nil {
return nil, err
}
log.Debug("getLocalPeerInfo", "GetMempoolSize", "after")
meminfo := resp.GetData().(*pb.MempoolSize)
var localpeerinfo pb.P2PPeerInfo
_, pub := m.network.node.nodeInfo.addrBook.GetPrivPubKey()
log.Debug("getLocalPeerInfo", "EventGetLastHeader", "befor")
//get header
msg = client.NewMessage("blockchain", pb.EventGetLastHeader, nil)
err = client.SendTimeout(msg, true, time.Second*10)
if err != nil {
log.Error("getLocalPeerInfo blockchain", "Error", err.Error())
return nil, err
}
resp, err = client.WaitTimeout(msg, time.Second*30)
if err != nil {
return nil, err
}
log.Debug("getLocalPeerInfo", "EventGetLastHeader", "after")
header := resp.GetData().(*pb.Header)
localpeerinfo.Header = header
localpeerinfo.Name = pub
localpeerinfo.MempoolSize = int32(meminfo.GetSize())
if m.network.node.nodeInfo.GetExternalAddr().IP == nil {
localpeerinfo.Addr = m.network.node.nodeInfo.GetListenAddr().IP.String()
localpeerinfo.Port = int32(m.network.node.nodeInfo.GetListenAddr().Port)
} else {
localpeerinfo.Addr = m.network.node.nodeInfo.GetExternalAddr().IP.String()
localpeerinfo.Port = int32(m.network.node.nodeInfo.GetExternalAddr().Port)
}
return &localpeerinfo, nil
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"encoding/hex"
"fmt"
"net"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/33cn/chain33/p2p/utils"
"github.com/33cn/chain33/common/version"
pb "github.com/33cn/chain33/types"
"golang.org/x/net/context"
pr "google.golang.org/grpc/peer"
)
// P2pserver object information
type P2pserver struct {
imtx sync.Mutex //for inboundpeers
smtx sync.Mutex
node *Node
streams map[string]chan interface{}
inboundpeers map[string]*innerpeer
closed int32
}
type innerpeer struct {
addr string
name string
timestamp int64
softversion string
p2pversion int32
}
// Start p2pserver start
func (s *P2pserver) Start() {
s.manageStream()
}
// Close p2pserver close
func (s *P2pserver) Close() {
atomic.StoreInt32(&s.closed, 1)
}
// IsClose is p2pserver running
func (s *P2pserver) IsClose() bool {
return atomic.LoadInt32(&s.closed) == 1
}
// NewP2pServer produce a p2pserver
func NewP2pServer() *P2pserver {
return &P2pserver{
streams: make(map[string]chan interface{}),
inboundpeers: make(map[string]*innerpeer),
}
}
// Ping p2pserver ping
func (s *P2pserver) Ping(ctx context.Context, in *pb.P2PPing) (*pb.P2PPong, error) {
log.Debug("ping")
if !P2pComm.CheckSign(in) {
log.Error("Ping", "p2p server", "check sig err")
return nil, pb.ErrPing
}
peerIP, _, err := resolveClientNetAddr(ctx)
if err != nil {
log.Error("Ping", "get grpc peer addr err", err)
return nil, fmt.Errorf("get grpc peer addr err:%s", err.Error())
}
peeraddr := fmt.Sprintf("%s:%v", peerIP, in.Port)
remoteNetwork, err := NewNetAddressString(peeraddr)
if err == nil {
if !s.node.nodeInfo.blacklist.Has(peeraddr) {
s.node.nodeInfo.addrBook.AddAddress(remoteNetwork, nil)
}
}
log.Debug("Send Pong", "Nonce", in.GetNonce())
return &pb.P2PPong{Nonce: in.GetNonce()}, nil
}
// GetAddr get address
func (s *P2pserver) GetAddr(ctx context.Context, in *pb.P2PGetAddr) (*pb.P2PAddr, error) {
log.Debug("GETADDR", "RECV ADDR", in, "OutBound Len", s.node.Size())
var addrlist []string
peers, _ := s.node.GetActivePeers()
log.Debug("GetAddr", "GetPeers", peers)
for _, peer := range peers {
addrlist = append(addrlist, peer.Addr())
}
return &pb.P2PAddr{Nonce: in.Nonce, Addrlist: addrlist}, nil
}
// GetAddrList get address list , and height of address
func (s *P2pserver) GetAddrList(ctx context.Context, in *pb.P2PGetAddr) (*pb.P2PAddrList, error) {
_, infos := s.node.GetActivePeers()
var peerinfos []*pb.P2PPeerInfo
for _, info := range infos {
peerinfos = append(peerinfos, &pb.P2PPeerInfo{Addr: info.GetAddr(), Port: info.GetPort(), Name: info.GetName(), Header: info.GetHeader(),
MempoolSize: info.GetMempoolSize()})
}
return &pb.P2PAddrList{Nonce: in.Nonce, Peerinfo: peerinfos}, nil
}
// Version version
func (s *P2pserver) Version(ctx context.Context, in *pb.P2PVersion) (*pb.P2PVerAck, error) {
return &pb.P2PVerAck{Version: s.node.nodeInfo.channelVersion, Service: 6, Nonce: in.Nonce}, nil
}
// Version2 p2pserver version
func (s *P2pserver) Version2(ctx context.Context, in *pb.P2PVersion) (*pb.P2PVersion, error) {
channel, ver := utils.DecodeChannelVersion(in.GetVersion())
log.Debug("p2pServer Version2", "p2pChannel", channel, "p2p version", ver)
if !s.node.verifyP2PChannel(channel) {
return nil, pb.ErrP2PChannel
}
log.Debug("Version2", "before", "GetPrivPubKey")
_, pub := s.node.nodeInfo.addrBook.GetPrivPubKey()
log.Debug("Version2", "after", "GetPrivPubKey")
peerIP, _, err := resolveClientNetAddr(ctx)
if err != nil {
log.Error("Version2", "get grpc peer addr err", err)
return nil, fmt.Errorf("get grpc peer addr err:%s", err.Error())
}
//addrFrom:表示发送方外网地址,addrRecv:表示接收方外网地址
_, port, err := net.SplitHostPort(in.AddrFrom)
if err != nil {
return nil, fmt.Errorf("AddrFrom format err")
}
peerAddr := fmt.Sprintf("%v:%v", peerIP, port)
remoteNetwork, err := NewNetAddressString(peerAddr)
if err == nil {
if !s.node.nodeInfo.blacklist.Has(remoteNetwork.String()) {
s.node.nodeInfo.addrBook.AddAddress(remoteNetwork, nil)
}
}
return &pb.P2PVersion{Version: s.node.nodeInfo.channelVersion,
Service: int64(s.node.nodeInfo.ServiceTy()), Nonce: in.Nonce,
AddrFrom: in.AddrRecv, AddrRecv: fmt.Sprintf("%v:%v", peerIP, port), UserAgent: pub}, nil
}
// SoftVersion software version
func (s *P2pserver) SoftVersion(ctx context.Context, in *pb.P2PPing) (*pb.Reply, error) {
if !P2pComm.CheckSign(in) {
log.Error("Ping", "p2p server", "check sig err")
return nil, pb.ErrPing
}
ver := version.GetVersion()
return &pb.Reply{IsOk: true, Msg: []byte(ver)}, nil
}
// BroadCastTx broadcast transactions of p2pserver
func (s *P2pserver) BroadCastTx(ctx context.Context, in *pb.P2PTx) (*pb.Reply, error) {
log.Debug("p2pServer RECV TRANSACTION", "in", in)
client := s.node.nodeInfo.client
msg := client.NewMessage("mempool", pb.EventTx, in.Tx)
err := client.Send(msg, false)
if err != nil {
return nil, err
}
return &pb.Reply{IsOk: true, Msg: []byte("ok")}, nil
}
// GetBlocks get blocks of p2pserver
func (s *P2pserver) GetBlocks(ctx context.Context, in *pb.P2PGetBlocks) (*pb.P2PInv, error) {
channel, ver := utils.DecodeChannelVersion(in.GetVersion())
log.Debug("p2pServer GetBlocks", "p2pChannel", channel, "p2p version", ver)
if !s.node.verifyP2PChannel(channel) {
return nil, pb.ErrP2PChannel
}
client := s.node.nodeInfo.client
msg := client.NewMessage("blockchain", pb.EventGetHeaders, &pb.ReqBlocks{Start: in.StartHeight, End: in.EndHeight,
IsDetail: false})
err := client.SendTimeout(msg, true, time.Minute)
if err != nil {
log.Error("GetBlocks", "Error", err.Error())
return nil, err
}
resp, err := client.WaitTimeout(msg, time.Minute)
if err != nil {
return nil, err
}
headers := resp.Data.(*pb.Headers)
var invs = make([]*pb.Inventory, 0)
for _, item := range headers.Items {
var inv pb.Inventory
inv.Ty = msgBlock
inv.Height = item.GetHeight()
invs = append(invs, &inv)
}
return &pb.P2PInv{Invs: invs}, nil
}
// GetMemPool p2pserver queries the local mempool
func (s *P2pserver) GetMemPool(ctx context.Context, in *pb.P2PGetMempool) (*pb.P2PInv, error) {
channel, ver := utils.DecodeChannelVersion(in.GetVersion())
log.Debug("p2pServer GetMemPool", "p2pChannel", channel, "p2p version", ver)
if !s.node.verifyP2PChannel(channel) {
return nil, pb.ErrP2PChannel
}
memtx, err := s.loadMempool()
if err != nil {
return nil, err
}
var invlist = make([]*pb.Inventory, 0)
for _, tx := range memtx {
invlist = append(invlist, &pb.Inventory{Hash: tx.Hash(), Ty: msgTx})
}
return &pb.P2PInv{Invs: invlist}, nil
}
// GetData get data of p2pserver
func (s *P2pserver) GetData(in *pb.P2PGetData, stream pb.P2Pgservice_GetDataServer) error {
channel, ver := utils.DecodeChannelVersion(in.GetVersion())
log.Debug("p2pServer Recv GetDataTx", "p2pChannel", channel, "p2p version", ver)
if !s.node.verifyP2PChannel(channel) {
return pb.ErrP2PChannel
}
var p2pInvData = make([]*pb.InvData, 0)
var count = 0
invs := in.GetInvs()
client := s.node.nodeInfo.client
for _, inv := range invs { //过滤掉不需要的数据
var invdata pb.InvData
var memtx = make(map[string]*pb.Transaction)
if inv.GetTy() == msgTx {
//loadMempool
if count == 0 {
var err error
memtx, err = s.loadMempool()
if err != nil {
continue
}
}
count++
txhash := hex.EncodeToString(inv.GetHash())
if tx, ok := memtx[txhash]; ok {
invdata.Value = &pb.InvData_Tx{Tx: tx}
invdata.Ty = msgTx
p2pInvData = append(p2pInvData, &invdata)
}
} else if inv.GetTy() == msgBlock {
height := inv.GetHeight()
reqblock := &pb.ReqBlocks{Start: height, End: height}
msg := client.NewMessage("blockchain", pb.EventGetBlocks, reqblock)
err := client.Send(msg, true)
if err != nil {
log.Error("GetBlocks", "Error", err.Error())
return err //blockchain 模块关闭,直接返回,不需要continue
}
resp, err := client.WaitTimeout(msg, time.Second*20)
if err != nil {
log.Error("GetBlocks Err", "Err", err.Error())
return err
}
blocks := resp.Data.(*pb.BlockDetails)
for _, item := range blocks.Items {
invdata.Ty = msgBlock
invdata.Value = &pb.InvData_Block{Block: item.Block}
p2pInvData = append(p2pInvData, &invdata)
}
}
}
var counts int
for _, invdata := range p2pInvData {
counts++
var InvDatas []*pb.InvData
InvDatas = append(InvDatas, invdata)
err := stream.Send(&pb.InvDatas{Items: InvDatas})
if err != nil {
log.Error("sendBlock", "err", err.Error())
return err
}
}
log.Debug("sendblock", "count", counts, "invs", len(invs))
return nil
}
// GetHeaders ger headers of p2pServer
func (s *P2pserver) GetHeaders(ctx context.Context, in *pb.P2PGetHeaders) (*pb.P2PHeaders, error) {
channel, ver := utils.DecodeChannelVersion(in.GetVersion())
log.Debug("p2pServer GetHeaders", "p2pChannel", channel, "p2p version", ver)
if !s.node.verifyP2PChannel(channel) {
return nil, pb.ErrP2PChannel
}
if in.GetEndHeight()-in.GetStartHeight() > 2000 || in.GetEndHeight() < in.GetStartHeight() {
return nil, fmt.Errorf("out of range")
}
client := s.node.nodeInfo.client
msg := client.NewMessage("blockchain", pb.EventGetHeaders, &pb.ReqBlocks{Start: in.GetStartHeight(), End: in.GetEndHeight()})
err := client.SendTimeout(msg, true, time.Minute)
if err != nil {
log.Error("GetHeaders", "Error", err.Error())
return nil, err
}
resp, err := client.WaitTimeout(msg, time.Minute)
if err != nil {
return nil, err
}
headers := resp.GetData().(*pb.Headers)
return &pb.P2PHeaders{Headers: headers.GetItems()}, nil
}
// GetPeerInfo get peer information of p2pServer
func (s *P2pserver) GetPeerInfo(ctx context.Context, in *pb.P2PGetPeerInfo) (*pb.P2PPeerInfo, error) {
channel, ver := utils.DecodeChannelVersion(in.GetVersion())
log.Debug("p2pServer GetPeerInfo", "p2pChannel", channel, "p2p version", ver)
if !s.node.verifyP2PChannel(channel) {
return nil, pb.ErrP2PChannel
}
client := s.node.nodeInfo.client
log.Debug("GetPeerInfo", "GetMempoolSize", "befor")
msg := client.NewMessage("mempool", pb.EventGetMempoolSize, nil)
err := client.SendTimeout(msg, true, time.Minute)
if err != nil {
log.Error("GetPeerInfo mempool", "Error", err.Error())
return nil, err
}
log.Debug("GetPeerInfo", "GetMempoolSize", "after")
resp, err := client.WaitTimeout(msg, time.Second*10)
if err != nil {
return nil, err
}
meminfo := resp.GetData().(*pb.MempoolSize)
var peerinfo pb.P2PPeerInfo
_, pub := s.node.nodeInfo.addrBook.GetPrivPubKey()
log.Debug("GetPeerInfo", "EventGetLastHeader", "befor")
//get header
msg = client.NewMessage("blockchain", pb.EventGetLastHeader, nil)
err = client.SendTimeout(msg, true, time.Minute)
if err != nil {
log.Error("GetPeerInfo blockchain", "Error", err.Error())
return nil, err
}
resp, err = client.WaitTimeout(msg, time.Second*10)
if err != nil {
return nil, err
}
log.Debug("GetPeerInfo", "EventGetLastHeader", "after")
header := resp.GetData().(*pb.Header)
peerinfo.Header = header
peerinfo.Name = pub
peerinfo.MempoolSize = int32(meminfo.GetSize())
peerinfo.Addr = s.node.nodeInfo.GetExternalAddr().IP.String()
peerinfo.Port = int32(s.node.nodeInfo.GetExternalAddr().Port)
return &peerinfo, nil
}
// BroadCastBlock broadcast block of p2pserver
func (s *P2pserver) BroadCastBlock(ctx context.Context, in *pb.P2PBlock) (*pb.Reply, error) {
log.Debug("BroadCastBlock")
client := s.node.nodeInfo.client
msg := client.NewMessage("blockchain", pb.EventBroadcastAddBlock, in.GetBlock())
err := client.Send(msg, false)
if err != nil {
log.Error("BroadCastBlock", "Error", err.Error())
return nil, err
}
return &pb.Reply{IsOk: true, Msg: []byte("ok")}, nil
}
// ServerStreamSend serverstream send of p2pserver
func (s *P2pserver) ServerStreamSend(in *pb.P2PPing, stream pb.P2Pgservice_ServerStreamSendServer) error {
if len(s.getInBoundPeers()) > int(s.node.nodeInfo.cfg.InnerBounds) {
return fmt.Errorf("beyound max inbound num")
}
peerIP, _, err := resolveClientNetAddr(stream.Context())
if err != nil {
log.Error("ServerStreamSend", "get grpc peer addr err", err)
return fmt.Errorf("get grpc peer addr err:%s", err.Error())
}
peerAddr := fmt.Sprintf("%s:%v", peerIP, in.GetPort())
//等待ReadStream接收节点version信息
var peerInfo *innerpeer
var reTry int32
peerName := hex.EncodeToString(in.GetSign().GetPubkey())
//此处不能用IP:Port 作为key,因为存在内网多个节点共享一个IP的可能,用peerName 不会有这个问题
for ; peerInfo == nil || peerInfo.p2pversion == 0; peerInfo = s.getInBoundPeerInfo(peerName) {
time.Sleep(time.Second)
reTry++
if reTry > 5 { //如果一直不跳出循环,这个goroutine 一直存在,潜在的风险点
return fmt.Errorf("can not find peer:%v", peerAddr)
}
}
log.Debug("ServerStreamSend")
dataChain := s.addStreamHandler(peerName)
defer s.deleteStream(peerName, dataChain)
for data := range dataChain {
if s.IsClose() {
return fmt.Errorf("node close")
}
sendData, doSend := s.node.processSendP2P(data, peerInfo.p2pversion, peerName, peerInfo.addr)
if !doSend {
continue
}
err := stream.Send(sendData)
if err != nil {
return err
}
}
return nil
}
// ServerStreamRead server stream read of p2pserver
func (s *P2pserver) ServerStreamRead(stream pb.P2Pgservice_ServerStreamReadServer) error {
if len(s.getInBoundPeers()) > int(s.node.nodeInfo.cfg.InnerBounds) {
return fmt.Errorf("beyound max inbound num:%v>%v", len(s.getInBoundPeers()), int(s.node.nodeInfo.cfg.InnerBounds))
}
log.Debug("StreamRead")
peerIP, _, err := resolveClientNetAddr(stream.Context())
if err != nil {
log.Error("ServerStreamRead", "get grpc peer addr err", err)
return fmt.Errorf("get grpc peer addr err:%s", err.Error())
}
var peeraddr, peername string
//此处delete是defer调用, 提前绑定变量,需要传入指针, peeraddr的值才能被获取
defer s.deleteInBoundPeerInfo(&peername)
defer stream.SendAndClose(&pb.ReqNil{})
for {
if s.IsClose() {
return fmt.Errorf("node close")
}
in, err := stream.Recv()
if err != nil {
log.Error("ServerStreamRead", "Recv", err)
return err
}
if s.node.processRecvP2P(in, peername, s.pubToStream, peeraddr) {
} else if ver := in.GetVersion(); ver != nil {
//接收版本信息
peername = ver.GetPeername()
softversion := ver.GetSoftversion()
innerpeer := s.getInBoundPeerInfo(peername)
channel, p2pVersion := utils.DecodeChannelVersion(ver.GetP2Pversion())
if !s.node.verifyP2PChannel(channel) {
return pb.ErrP2PChannel
}
if innerpeer != nil {
//这里如果直接修改原值, 可能data race
info := *innerpeer
info.p2pversion = p2pVersion
info.softversion = softversion
s.addInBoundPeerInfo(peername, info)
} else {
//没有获取到peer 的信息,说明没有获取ping的消息包
return pb.ErrStreamPing
}
} else if ping := in.GetPing(); ping != nil { ///被远程节点初次连接后,会收到ping 数据包,收到后注册到inboundpeers.
//Ping package
if !P2pComm.CheckSign(ping) {
log.Error("ServerStreamRead", "check stream", "check sig err")
return pb.ErrStreamPing
}
if s.node.Size() > 0 {
if peerIP != s.node.nodeInfo.GetListenAddr().IP.String() && peerIP != s.node.nodeInfo.GetExternalAddr().IP.String() {
s.node.nodeInfo.SetServiceTy(Service)
}
}
peername = hex.EncodeToString(ping.GetSign().GetPubkey())
peeraddr = fmt.Sprintf("%s:%v", peerIP, ping.GetPort())
s.addInBoundPeerInfo(peername, innerpeer{addr: peeraddr, name: peername, timestamp: pb.Now().Unix()})
}
}
}
// CollectInPeers collect external network nodes of connect their own
func (s *P2pserver) CollectInPeers(ctx context.Context, in *pb.P2PPing) (*pb.PeerList, error) {
log.Debug("CollectInPeers")
if !P2pComm.CheckSign(in) {
log.Debug("CollectInPeers", "ping", "signatrue err")
return nil, pb.ErrPing
}
inPeers := s.getInBoundPeers()
var p2pPeers []*pb.Peer
for _, inpeer := range inPeers {
ip, portstr, err := net.SplitHostPort(inpeer.addr)
if err != nil {
continue
}
port, err := strconv.Atoi(portstr)
if err != nil {
continue
}
p2pPeers = append(p2pPeers, &pb.Peer{Name: inpeer.name, Addr: ip, Port: int32(port)}) ///仅用name,addr,port字段,用于统计peer num.
}
return &pb.PeerList{Peers: p2pPeers}, nil
}
// CollectInPeers2 collect external network nodes of connect their own
func (s *P2pserver) CollectInPeers2(ctx context.Context, in *pb.P2PPing) (*pb.PeersReply, error) {
log.Debug("CollectInPeers2")
if !P2pComm.CheckSign(in) {
log.Debug("CollectInPeers", "ping", "signatrue err")
return nil, pb.ErrPing
}
inPeers := s.getInBoundPeers()
var p2pPeers []*pb.PeersInfo
for _, inpeer := range inPeers {
ip, portstr, err := net.SplitHostPort(inpeer.addr)
if err != nil {
continue
}
port, err := strconv.Atoi(portstr)
if err != nil {
continue
}
p2pPeers = append(p2pPeers, &pb.PeersInfo{Name: inpeer.name, Ip: ip, Port: int32(port),
Softversion: inpeer.softversion, P2Pversion: inpeer.p2pversion}) ///仅用name,addr,port字段,用于统计peer num.
}
return &pb.PeersReply{Peers: p2pPeers}, nil
}
func (s *P2pserver) loadMempool() (map[string]*pb.Transaction, error) {
var txmap = make(map[string]*pb.Transaction)
client := s.node.nodeInfo.client
msg := client.NewMessage("mempool", pb.EventGetMempool, nil)
err := client.SendTimeout(msg, true, time.Minute)
if err != nil {
log.Error("loadMempool", "Error", err.Error())
return txmap, err
}
resp, err := client.WaitTimeout(msg, time.Minute)
if err != nil {
return txmap, err
}
txlist := resp.GetData().(*pb.ReplyTxList)
txs := txlist.GetTxs()
for _, tx := range txs {
txmap[hex.EncodeToString(tx.Hash())] = tx
}
return txmap, nil
}
func (s *P2pserver) manageStream() {
go func() { //发送空的block stream ping
ticker := time.NewTicker(StreamPingTimeout)
defer ticker.Stop()
for {
if s.IsClose() {
return
}
<-ticker.C
s.pubToAllStream(&pb.P2PPing{})
}
}()
go func() {
fifoChan := s.node.pubsub.Sub("block", "tx")
for data := range fifoChan {
if s.IsClose() {
return
}
s.pubToAllStream(data)
}
log.Info("p2pserver", "manageStream", "close")
}()
}
func (s *P2pserver) addStreamHandler(peerName string) chan interface{} {
s.smtx.Lock()
defer s.smtx.Unlock()
if dataChan, ok := s.streams[peerName]; ok {
//一个节点对应一个流, 重复打开两个流, 关闭老的数据管道
close(dataChan)
}
s.streams[peerName] = make(chan interface{}, 1024)
return s.streams[peerName]
}
//发布数据到所有服务流
func (s *P2pserver) pubToAllStream(data interface{}) {
s.smtx.Lock()
defer s.smtx.Unlock()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for _, dataChan := range s.streams {
select {
case dataChan <- data:
case <-ticker.C:
continue
}
}
}
//发布数据到指定流
func (s *P2pserver) pubToStream(data interface{}, peerName string) {
s.smtx.Lock()
defer s.smtx.Unlock()
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
if dataChan, ok := s.streams[peerName]; ok {
select {
case dataChan <- data:
case <-ticker.C:
return
}
}
}
func (s *P2pserver) deleteStream(peerName string, delChan chan interface{}) {
s.smtx.Lock()
defer s.smtx.Unlock()
if dataChan, ok := s.streams[peerName]; ok && dataChan == delChan {
close(s.streams[peerName])
delete(s.streams, peerName)
}
}
func (s *P2pserver) addInBoundPeerInfo(peerName string, info innerpeer) {
s.imtx.Lock()
defer s.imtx.Unlock()
s.inboundpeers[peerName] = &info
}
func (s *P2pserver) deleteInBoundPeerInfo(peerName *string) {
s.imtx.Lock()
defer s.imtx.Unlock()
delete(s.inboundpeers, *peerName)
}
func (s *P2pserver) getInBoundPeerInfo(peerName string) *innerpeer {
s.imtx.Lock()
defer s.imtx.Unlock()
if key, ok := s.inboundpeers[peerName]; ok {
return key
}
return nil
}
func (s *P2pserver) getInBoundPeers() []*innerpeer {
s.imtx.Lock()
defer s.imtx.Unlock()
var peers []*innerpeer
for _, innerpeer := range s.inboundpeers {
peers = append(peers, innerpeer)
}
return peers
}
func resolveClientNetAddr(ctx context.Context) (host, port string, err error) {
grpcPeer, ok := pr.FromContext(ctx)
if ok {
return net.SplitHostPort(grpcPeer.Addr.String())
}
return "", "", fmt.Errorf("get grpc peer from ctx err")
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"strings"
"sync"
"sync/atomic"
"time"
v "github.com/33cn/chain33/common/version"
pb "github.com/33cn/chain33/types"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
// Start peer start
func (p *Peer) Start() {
log.Debug("Peer", "Start", p.Addr())
go p.heartBeat()
}
// Close peer close
func (p *Peer) Close() {
//避免重复关闭
if !atomic.CompareAndSwapInt32(&p.isclose, 0, 1) {
return
}
p.mconn.Close()
if p.taskChan != nil {
//unsub all topics
p.node.pubsub.Unsub(p.taskChan)
}
log.Info("Peer", "closed", p.Addr())
}
// Peer object information
type Peer struct {
mutex sync.RWMutex
node *Node
conn *grpc.ClientConn // source connection
persistent bool
isclose int32
version *Version
name string //远程节点的name
mconn *MConnection
peerAddr *NetAddress
peerStat *Stat
taskChan chan interface{} //tx block
inBounds int32 //连接此节点的客户端节点数量
IsMaxInbouds bool
}
// NewPeer produce a peer object
func NewPeer(conn *grpc.ClientConn, node *Node, remote *NetAddress) *Peer {
p := &Peer{
conn: conn,
node: node,
}
p.peerStat = new(Stat)
p.version = new(Version)
p.version.SetSupport(true)
p.mconn = NewMConnection(conn, remote, p)
return p
}
// Version version object information
type Version struct {
mtx sync.Mutex
version int32
versionSupport bool
}
// Stat object information
type Stat struct {
mtx sync.Mutex
ok bool
}
// Ok start is ok
func (st *Stat) Ok() {
st.mtx.Lock()
defer st.mtx.Unlock()
st.ok = true
}
// NotOk start is not ok
func (st *Stat) NotOk() {
st.mtx.Lock()
defer st.mtx.Unlock()
st.ok = false
}
// IsOk start is ok or not
func (st *Stat) IsOk() bool {
st.mtx.Lock()
defer st.mtx.Unlock()
return st.ok
}
// SetSupport set support of version
func (v *Version) SetSupport(ok bool) {
v.mtx.Lock()
defer v.mtx.Unlock()
v.versionSupport = ok
}
// IsSupport is support version
func (v *Version) IsSupport() bool {
v.mtx.Lock()
defer v.mtx.Unlock()
return v.versionSupport
}
// SetVersion set version number
func (v *Version) SetVersion(ver int32) {
v.mtx.Lock()
defer v.mtx.Unlock()
v.version = ver
}
// GetVersion get version number
func (v *Version) GetVersion() int32 {
v.mtx.Lock()
defer v.mtx.Unlock()
return v.version
}
func (p *Peer) heartBeat() {
pcli := NewNormalP2PCli()
for {
if !p.GetRunning() {
return
}
peername, err := pcli.SendVersion(p, p.node.nodeInfo)
P2pComm.CollectPeerStat(err, p)
if err != nil || peername == "" {
//版本不对,直接关掉
log.Error("PeerHeartBeatSendVersion", "peerName", peername, "err", err)
p.Close()
return
}
log.Debug("sendVersion", "peer name", peername)
p.SetPeerName(peername) //设置连接的远程节点的节点名称
p.taskChan = p.node.pubsub.Sub("block", "tx", peername)
go p.sendStream()
go p.readStream()
break
}
ticker := time.NewTicker(PingTimeout)
defer ticker.Stop()
for {
if !p.GetRunning() {
return
}
peerNum, err := pcli.GetInPeersNum(p)
if err == nil {
atomic.StoreInt32(&p.inBounds, int32(peerNum))
}
err = pcli.SendPing(p, p.node.nodeInfo)
if err != nil {
log.Error("SendPeerPing", "peer", p.Addr(), "err", err)
}
<-ticker.C
}
}
// GetInBouns get inbounds of peer
func (p *Peer) GetInBouns() int32 {
return atomic.LoadInt32(&p.inBounds)
}
// GetPeerInfo get peer information of peer
func (p *Peer) GetPeerInfo() (*pb.P2PPeerInfo, error) {
return p.mconn.gcli.GetPeerInfo(context.Background(), &pb.P2PGetPeerInfo{Version: p.node.nodeInfo.channelVersion}, grpc.FailFast(true))
}
func (p *Peer) sendStream() {
//Stream Send data
for {
if !p.GetRunning() {
log.Debug("sendStream peer connect closed", "peerid", p.GetPeerName())
return
}
ctx, cancel := context.WithCancel(context.Background())
resp, err := p.mconn.gcli.ServerStreamRead(ctx)
P2pComm.CollectPeerStat(err, p)
if err != nil {
cancel()
log.Error("sendStream", "ServerStreamRead", err)
time.Sleep(time.Second * 5)
continue
}
//send ping package
ping, err := P2pComm.NewPingData(p.node.nodeInfo)
if err != nil {
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
cancel()
time.Sleep(time.Second)
continue
}
p2pdata := new(pb.BroadCastData)
p2pdata.Value = &pb.BroadCastData_Ping{Ping: ping}
if err := resp.Send(p2pdata); err != nil {
P2pComm.CollectPeerStat(err, p)
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
cancel()
log.Error("sendStream", "sendping", err)
time.Sleep(time.Second)
continue
}
//send softversion&p2pversion
_, peerName := p.node.nodeInfo.addrBook.GetPrivPubKey()
p2pdata.Value = &pb.BroadCastData_Version{Version: &pb.Versions{P2Pversion: p.node.nodeInfo.channelVersion,
Softversion: v.GetVersion(), Peername: peerName}}
if err := resp.Send(p2pdata); err != nil {
P2pComm.CollectPeerStat(err, p)
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
cancel()
log.Error("sendStream", "sendversion", err)
continue
}
timeout := time.NewTimer(time.Second * 2)
defer timeout.Stop()
SEND_LOOP:
for {
if !p.GetRunning() {
return
}
select {
case task := <-p.taskChan:
if !p.GetRunning() {
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
cancel()
log.Error("sendStream peer connect closed", "peerName", p.GetPeerName())
return
}
sendData, doSend := p.node.processSendP2P(task, p.version.GetVersion(), p.GetPeerName(), p.Addr())
if !doSend {
continue
}
err := resp.Send(sendData)
P2pComm.CollectPeerStat(err, p)
if err != nil {
log.Error("sendStream", "send", err)
if grpc.Code(err) == codes.Unimplemented { //maybe order peers delete peer to BlackList
p.node.nodeInfo.blacklist.Add(p.Addr(), 3600)
}
time.Sleep(time.Second) //have a rest
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
cancel()
break SEND_LOOP //下一次外循环重新获取stream
}
log.Debug("sendStream", "send data", "ok")
case <-timeout.C:
if !p.GetRunning() {
log.Error("sendStream timeout")
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
cancel()
return
}
timeout.Reset(time.Second * 2)
}
}
}
}
func (p *Peer) readStream() {
for {
if !p.GetRunning() {
log.Debug("readstream", "loop", "done")
return
}
ping, err := P2pComm.NewPingData(p.node.nodeInfo)
if err != nil {
log.Error("readStream", "err:", err.Error(), "peerIp", p.Addr())
continue
}
resp, err := p.mconn.gcli.ServerStreamSend(context.Background(), ping)
P2pComm.CollectPeerStat(err, p)
if err != nil {
log.Error("readStream", "serverstreamsend,err:", err, "peer", p.Addr())
time.Sleep(time.Second)
continue
}
log.Debug("SubStreamBlock", "Start", p.Addr())
for {
if !p.GetRunning() {
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
return
}
data, err := resp.Recv()
if err != nil {
P2pComm.CollectPeerStat(err, p)
log.Error("readStream", "recv,err:", err.Error(), "peerAddr", p.Addr())
errs := resp.CloseSend()
if errs != nil {
log.Error("CloseSend", "err", errs)
}
log.Error("readStream", "recv,err:", err.Error(), "peerIp", p.Addr())
if grpc.Code(err) == codes.Unimplemented { //maybe order peers delete peer to BlackList
p.node.nodeInfo.blacklist.Add(p.Addr(), 3600)
return
}
//beyound max inbound num
if strings.Contains(err.Error(), "beyound max inbound num") {
log.Debug("readStream", "peer inbounds num", p.GetInBouns())
p.IsMaxInbouds = true
P2pComm.CollectPeerStat(err, p)
return
}
time.Sleep(time.Second) //have a rest
}
p.node.processRecvP2P(data, p.GetPeerName(), p.node.pubToPeer, p.Addr())
}
}
}
// GetRunning get running ok or not
func (p *Peer) GetRunning() bool {
if p.node.isClose() {
return false
}
return atomic.LoadInt32(&p.isclose) != 1
}
// MakePersistent marks the peer as persistent.
func (p *Peer) MakePersistent() {
p.persistent = true
}
// SetAddr set address of peer
func (p *Peer) SetAddr(addr *NetAddress) {
p.peerAddr = addr
}
// Addr returns peer's remote network address.
func (p *Peer) Addr() string {
return p.peerAddr.String()
}
// IsPersistent returns true if the peer is persitent, false otherwise.
func (p *Peer) IsPersistent() bool {
return p.persistent
}
// SetPeerName set name of peer
func (p *Peer) SetPeerName(name string) {
p.mutex.Lock()
defer p.mutex.Unlock()
if name == "" {
return
}
p.name = name
}
// GetPeerName get name of peer
func (p *Peer) GetPeerName() string {
p.mutex.RLock()
defer p.mutex.RUnlock()
return p.name
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"bytes"
"encoding/hex"
"time"
"github.com/33cn/chain33/p2p/utils"
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/types"
)
var (
//接收交易和区块过滤缓存, 避免重复提交到mempool或blockchain
txHashFilter = utils.NewFilter(TxRecvFilterCacheNum)
blockHashFilter = utils.NewFilter(BlockFilterCacheNum)
//发送交易和区块时过滤缓存, 解决冗余广播发送
txSendFilter = utils.NewFilter(TxSendFilterCacheNum)
blockSendFilter = utils.NewFilter(BlockFilterCacheNum)
//发送交易短哈希广播,在本地暂时缓存一些区块数据, 限制最大大小
totalBlockCache = utils.NewSpaceLimitCache(BlockCacheNum, MaxBlockCacheByteSize)
//接收到短哈希区块数据,只构建出区块部分交易,需要缓存, 并继续向对端节点请求剩余数据
ltBlockCache = utils.NewSpaceLimitCache(BlockCacheNum/2, MaxBlockCacheByteSize/2)
)
type sendFilterInfo struct {
//记录广播交易或区块时需要忽略的节点, 这些节点可能是交易的来源节点,也可能节点间维护了多条连接, 冗余发送
ignoreSendPeers map[string]bool
}
type pubFuncType func(interface{}, string)
func (n *Node) pubToPeer(data interface{}, pid string) {
n.pubsub.FIFOPub(data, pid)
}
func (n *Node) processSendP2P(rawData interface{}, peerVersion int32, pid, peerAddr string) (sendData *types.BroadCastData, doSend bool) {
//出错处理
defer func() {
if r := recover(); r != nil {
log.Error("processSendP2P_Panic", "sendData", rawData, "peerAddr", peerAddr, "recoverErr", r)
doSend = false
}
}()
log.Debug("ProcessSendP2PBegin", "peerID", pid, "peerAddr", peerAddr)
sendData = &types.BroadCastData{}
doSend = false
if tx, ok := rawData.(*types.P2PTx); ok {
doSend = n.sendTx(tx, sendData, peerVersion, pid, peerAddr)
} else if blc, ok := rawData.(*types.P2PBlock); ok {
doSend = n.sendBlock(blc, sendData, peerVersion, pid, peerAddr)
} else if query, ok := rawData.(*types.P2PQueryData); ok {
doSend = n.sendQueryData(query, sendData, peerAddr)
} else if rep, ok := rawData.(*types.P2PBlockTxReply); ok {
doSend = n.sendQueryReply(rep, sendData, peerAddr)
} else if ping, ok := rawData.(*types.P2PPing); ok {
doSend = true
sendData.Value = &types.BroadCastData_Ping{Ping: ping}
}
log.Debug("ProcessSendP2PEnd", "peerAddr", peerAddr, "doSend", doSend)
return
}
func (n *Node) processRecvP2P(data *types.BroadCastData, pid string, pubPeerFunc pubFuncType, peerAddr string) (handled bool) {
//接收网络数据不可靠
defer func() {
if r := recover(); r != nil {
log.Error("ProcessRecvP2P_Panic", "recvData", data, "peerAddr", peerAddr, "recoverErr", r)
}
}()
log.Debug("ProcessRecvP2P", "peerID", pid, "peerAddr", peerAddr)
if pid == "" {
return false
}
handled = true
if tx := data.GetTx(); tx != nil {
n.recvTx(tx, pid, peerAddr)
} else if ltTx := data.GetLtTx(); ltTx != nil {
n.recvLtTx(ltTx, pid, peerAddr, pubPeerFunc)
} else if ltBlc := data.GetLtBlock(); ltBlc != nil {
n.recvLtBlock(ltBlc, pid, peerAddr, pubPeerFunc)
} else if blc := data.GetBlock(); blc != nil {
n.recvBlock(blc, pid, peerAddr)
} else if query := data.GetQuery(); query != nil {
n.recvQueryData(query, pid, peerAddr, pubPeerFunc)
} else if rep := data.GetBlockRep(); rep != nil {
n.recvQueryReply(rep, pid, peerAddr, pubPeerFunc)
} else {
handled = false
}
log.Debug("ProcessRecvP2P", "peerAddr", peerAddr, "handled", handled)
return
}
func (n *Node) sendBlock(block *types.P2PBlock, p2pData *types.BroadCastData, peerVersion int32, pid, peerAddr string) (doSend bool) {
byteHash := block.Block.Hash(n.chainCfg)
blockHash := hex.EncodeToString(byteHash)
//检测冗余发送
ignoreSend := n.addIgnoreSendPeerAtomic(blockSendFilter, blockHash, pid)
log.Debug("P2PSendBlock", "blockHash", blockHash, "peerIsLtVersion", peerVersion >= lightBroadCastVersion,
"peerAddr", peerAddr, "ignoreSend", ignoreSend)
if ignoreSend {
return false
}
if peerVersion >= lightBroadCastVersion && len(block.Block.Txs) >= int(n.nodeInfo.cfg.MinLtBlockTxNum) {
ltBlock := &types.LightBlock{}
ltBlock.Size = int64(types.Size(block.Block))
ltBlock.Header = block.Block.GetHeader(n.chainCfg)
ltBlock.Header.Hash = byteHash[:]
ltBlock.Header.Signature = block.Block.Signature
ltBlock.MinerTx = block.Block.Txs[0]
for _, tx := range block.Block.Txs[1:] {
//tx short hash
ltBlock.STxHashes = append(ltBlock.STxHashes, types.CalcTxShortHash(tx.Hash()))
}
// cache block
if !totalBlockCache.Contains(blockHash) {
totalBlockCache.Add(blockHash, block.Block, ltBlock.Size)
}
p2pData.Value = &types.BroadCastData_LtBlock{LtBlock: ltBlock}
} else {
p2pData.Value = &types.BroadCastData_Block{Block: block}
}
return true
}
func (n *Node) sendQueryData(query *types.P2PQueryData, p2pData *types.BroadCastData, peerAddr string) bool {
log.Debug("P2PSendQueryData", "peerAddr", peerAddr)
p2pData.Value = &types.BroadCastData_Query{Query: query}
return true
}
func (n *Node) sendQueryReply(rep *types.P2PBlockTxReply, p2pData *types.BroadCastData, peerAddr string) bool {
log.Debug("P2PSendQueryReply", "peerAddr", peerAddr)
p2pData.Value = &types.BroadCastData_BlockRep{BlockRep: rep}
return true
}
func (n *Node) sendTx(tx *types.P2PTx, p2pData *types.BroadCastData, peerVersion int32, pid, peerAddr string) (doSend bool) {
txHash := hex.EncodeToString(tx.Tx.Hash())
ttl := tx.GetRoute().GetTTL()
isLightSend := peerVersion >= lightBroadCastVersion && ttl >= n.nodeInfo.cfg.LightTxTTL
//检测冗余发送
ignoreSend := false
//短哈希广播不记录发送过滤
if !isLightSend {
ignoreSend = n.addIgnoreSendPeerAtomic(txSendFilter, txHash, pid)
}
log.Debug("P2PSendTx", "txHash", txHash, "ttl", ttl, "isLightSend", isLightSend,
"peerAddr", peerAddr, "ignoreSend", ignoreSend)
if ignoreSend {
return false
}
//超过最大的ttl, 不再发送
if ttl > n.nodeInfo.cfg.MaxTTL {
return false
}
//新版本且ttl达到设定值
if isLightSend {
p2pData.Value = &types.BroadCastData_LtTx{ //超过最大的ttl, 不再发送
LtTx: &types.LightTx{
TxHash: tx.Tx.Hash(),
Route: tx.GetRoute(),
},
}
} else {
p2pData.Value = &types.BroadCastData_Tx{Tx: tx}
}
return true
}
func (n *Node) recvTx(tx *types.P2PTx, pid, peerAddr string) {
if tx.GetTx() == nil {
return
}
txHash := hex.EncodeToString(tx.GetTx().Hash())
//将节点id添加到发送过滤, 避免冗余发送
n.addIgnoreSendPeerAtomic(txSendFilter, txHash, pid)
//重复接收
isDuplicate := txHashFilter.AddWithCheckAtomic(txHash, true)
log.Debug("recvTx", "tx", txHash, "ttl", tx.GetRoute().GetTTL(), "peerAddr", peerAddr, "duplicateTx", isDuplicate)
if isDuplicate {
return
}
//有可能收到老版本的交易路由,此时route是空指针
if tx.GetRoute() == nil {
tx.Route = &types.P2PRoute{TTL: 1}
}
txHashFilter.Add(txHash, tx.GetRoute())
errs := n.postMempool(txHash, tx.GetTx())
if errs != nil {
log.Error("recvTx", "process post mempool EventTx msg Error", errs.Error())
}
}
func (n *Node) recvLtTx(tx *types.LightTx, pid, peerAddr string, pubPeerFunc pubFuncType) {
txHash := hex.EncodeToString(tx.TxHash)
//将节点id添加到发送过滤, 避免冗余发送
n.addIgnoreSendPeerAtomic(txSendFilter, txHash, pid)
exist := txHashFilter.Contains(txHash)
log.Debug("recvLtTx", "txHash", txHash, "ttl", tx.GetRoute().GetTTL(), "peerAddr", peerAddr, "exist", exist)
//本地不存在, 需要向对端节点发起完整交易请求. 如果存在则表示本地已经接收过此交易, 不做任何操作
if !exist {
query := &types.P2PQueryData{}
query.Value = &types.P2PQueryData_TxReq{
TxReq: &types.P2PTxReq{
TxHash: tx.TxHash,
},
}
//发布到指定的节点
pubPeerFunc(query, pid)
}
}
func (n *Node) recvBlock(block *types.P2PBlock, pid, peerAddr string) {
if block.GetBlock() == nil {
return
}
blockHash := hex.EncodeToString(block.GetBlock().Hash(n.chainCfg))
//将节点id添加到发送过滤, 避免冗余发送
n.addIgnoreSendPeerAtomic(blockSendFilter, blockHash, pid)
//如果重复接收, 则不再发到blockchain执行
isDuplicate := blockHashFilter.AddWithCheckAtomic(blockHash, true)
log.Debug("recvBlock", "blockHeight", block.GetBlock().GetHeight(), "peerAddr", peerAddr,
"block size(KB)", float32(block.Block.Size())/1024, "blockHash", blockHash, "duplicateBlock", isDuplicate)
if isDuplicate {
return
}
//发送至blockchain执行
if err := n.postBlockChain(blockHash, pid, block.GetBlock()); err != nil {
log.Error("recvBlock", "send block to blockchain Error", err.Error())
}
}
func (n *Node) recvLtBlock(ltBlock *types.LightBlock, pid, peerAddr string, pubPeerFunc pubFuncType) {
blockHash := hex.EncodeToString(ltBlock.Header.Hash)
//将节点id添加到发送过滤, 避免冗余发送
n.addIgnoreSendPeerAtomic(blockSendFilter, blockHash, pid)
//检测是否已经收到此block
isDuplicate := blockHashFilter.AddWithCheckAtomic(blockHash, true)
log.Debug("recvLtBlock", "blockHash", blockHash, "blockHeight", ltBlock.GetHeader().GetHeight(),
"peerAddr", peerAddr, "duplicateBlock", isDuplicate)
if isDuplicate {
return
}
//组装block
block := &types.Block{}
block.TxHash = ltBlock.Header.TxHash
block.Signature = ltBlock.Header.Signature
block.ParentHash = ltBlock.Header.ParentHash
block.Height = ltBlock.Header.Height
block.BlockTime = ltBlock.Header.BlockTime
block.Difficulty = ltBlock.Header.Difficulty
block.Version = ltBlock.Header.Version
block.StateHash = ltBlock.Header.StateHash
//add miner tx
block.Txs = append(block.Txs, ltBlock.MinerTx)
txList := &types.ReplyTxList{}
ok := false
//get tx list from mempool
if len(ltBlock.STxHashes) > 0 {
resp, err := n.queryMempool(types.EventTxListByHash, &types.ReqTxHashList{Hashes: ltBlock.STxHashes, IsShortHash: true})
if err != nil {
log.Error("recvLtBlock", "queryTxListByHashErr", err)
return
}
txList, ok = resp.(*types.ReplyTxList)
if !ok {
log.Error("recvLtBlock", "queryMemPool", "nilReplyTxList")
}
}
nilTxIndices := make([]int32, 0)
for i := 0; ok && i < len(txList.Txs); i++ {
tx := txList.Txs[i]
if tx == nil {
//tx not exist in mempool
nilTxIndices = append(nilTxIndices, int32(i+1))
tx = &types.Transaction{}
} else if count := tx.GetGroupCount(); count > 0 {
group, err := tx.GetTxGroup()
if err != nil {
log.Error("recvLtBlock", "getTxGroupErr", err)
//触发请求所有
nilTxIndices = nilTxIndices[:0]
break
}
block.Txs = append(block.Txs, group.Txs...)
//跳过遍历
i += len(group.Txs) - 1
continue
}
block.Txs = append(block.Txs, tx)
}
nilTxLen := len(nilTxIndices)
//需要比较交易根哈希是否一致, 不一致需要请求区块内所有的交易
if nilTxLen == 0 && len(block.Txs) == int(ltBlock.Header.TxCount) {
if bytes.Equal(block.TxHash, merkle.CalcMerkleRoot(n.chainCfg, block.Height, block.Txs)) {
log.Debug("recvLtBlock", "height", block.GetHeight(), "peerAddr", peerAddr,
"blockHash", blockHash, "block size(KB)", float32(ltBlock.Size)/1024)
//发送至blockchain执行
if err := n.postBlockChain(blockHash, pid, block); err != nil {
log.Error("recvLtBlock", "send block to blockchain Error", err.Error())
}
return
}
log.Debug("recvLtBlock:TxHashCheckFail", "height", block.GetHeight(), "peerAddr", peerAddr,
"blockHash", blockHash, "block.Txs", block.Txs)
}
// 缺失的交易个数大于总数1/3 或者缺失数据大小大于2/3, 触发请求区块所有交易数据
if nilTxLen > 0 && (float32(nilTxLen) > float32(ltBlock.Header.TxCount)/3 ||
float32(block.Size()) < float32(ltBlock.Size)/3) {
nilTxIndices = nilTxIndices[:0]
}
log.Debug("recvLtBlock", "queryBlockHash", blockHash, "queryHeight", ltBlock.GetHeader().GetHeight(), "queryTxNum", len(nilTxIndices))
// query not exist txs
query := &types.P2PQueryData{
Value: &types.P2PQueryData_BlockTxReq{
BlockTxReq: &types.P2PBlockTxReq{
BlockHash: blockHash,
TxIndices: nilTxIndices,
},
},
}
//pub to specified peer
pubPeerFunc(query, pid)
//需要将不完整的block预存
ltBlockCache.Add(blockHash, block, int64(block.Size()))
}
func (n *Node) recvQueryData(query *types.P2PQueryData, pid, peerAddr string, pubPeerFunc pubFuncType) {
if txReq := query.GetTxReq(); txReq != nil {
txHash := hex.EncodeToString(txReq.TxHash)
log.Debug("recvQueryTx", "txHash", txHash, "peerAddr", peerAddr)
//向mempool请求交易
resp, err := n.queryMempool(types.EventTxListByHash, &types.ReqTxHashList{Hashes: []string{string(txReq.TxHash)}})
if err != nil {
log.Error("recvQuery", "queryMempoolErr", err)
return
}
txList, _ := resp.(*types.ReplyTxList)
//返回的数据检测
if len(txList.GetTxs()) != 1 || txList.GetTxs()[0] == nil {
log.Error("recvQueryTx", "txHash", txHash, "err", "recvNilTxFromMempool")
return
}
p2pTx := &types.P2PTx{Tx: txList.Txs[0]}
//再次发送完整交易至节点, ttl重设为1
p2pTx.Route = &types.P2PRoute{TTL: 1}
pubPeerFunc(p2pTx, pid)
} else if blcReq := query.GetBlockTxReq(); blcReq != nil {
log.Debug("recvQueryBlockTx", "blockHash", blcReq.BlockHash, "queryTxCount", len(blcReq.TxIndices), "peerAddr", peerAddr)
if block, ok := totalBlockCache.Get(blcReq.BlockHash).(*types.Block); ok {
blockRep := &types.P2PBlockTxReply{BlockHash: blcReq.BlockHash}
blockRep.TxIndices = blcReq.TxIndices
for _, idx := range blcReq.TxIndices {
blockRep.Txs = append(blockRep.Txs, block.Txs[idx])
}
//请求所有的交易
if len(blockRep.TxIndices) == 0 {
blockRep.Txs = block.Txs
}
pubPeerFunc(blockRep, pid)
}
}
}
func (n *Node) recvQueryReply(rep *types.P2PBlockTxReply, pid, peerAddr string, pubPeerFunc pubFuncType) {
log.Debug("recvQueryReplyBlock", "blockHash", rep.GetBlockHash(), "queryTxsCount", len(rep.GetTxIndices()), "peerAddr", peerAddr)
val, exist := ltBlockCache.Remove(rep.BlockHash)
block, _ := val.(*types.Block)
//not exist in cache or nil block
if !exist || block == nil {
return
}
for i, idx := range rep.TxIndices {
block.Txs[idx] = rep.Txs[i]
}
//所有交易覆盖
if len(rep.TxIndices) == 0 {
block.Txs = rep.Txs
}
//计算的root hash是否一致
if bytes.Equal(block.TxHash, merkle.CalcMerkleRoot(n.chainCfg, block.Height, block.Txs)) {
log.Debug("recvQueryReplyBlock", "blockHeight", block.GetHeight(), "peerAddr", peerAddr,
"block size(KB)", float32(block.Size())/1024, "blockHash", rep.BlockHash)
//发送至blockchain执行
if err := n.postBlockChain(rep.BlockHash, pid, block); err != nil {
log.Error("recvQueryReplyBlock", "send block to blockchain Error", err.Error())
}
} else if len(rep.TxIndices) != 0 {
log.Debug("recvQueryReplyBlock", "GetTotalBlock", block.GetHeight())
//不一致尝试请求整个区块的交易, 且判定是否已经请求过完整交易
query := &types.P2PQueryData{
Value: &types.P2PQueryData_BlockTxReq{
BlockTxReq: &types.P2PBlockTxReq{
BlockHash: rep.BlockHash,
TxIndices: nil,
},
},
}
//pub to specified peer
pubPeerFunc(query, pid)
block.Txs = nil
ltBlockCache.Add(rep.BlockHash, block, int64(block.Size()))
}
}
func (n *Node) queryMempool(ty int64, data interface{}) (interface{}, error) {
client := n.nodeInfo.client
msg := client.NewMessage("mempool", ty, data)
err := client.Send(msg, true)
if err != nil {
return nil, err
}
resp, err := client.WaitTimeout(msg, time.Second*10)
if err != nil {
return nil, err
}
return resp.Data, nil
}
func (n *Node) postBlockChain(blockHash, pid string, block *types.Block) error {
return n.p2pMgr.PubBroadCast(blockHash, &types.BlockPid{Pid: pid, Block: block}, types.EventBroadcastAddBlock)
}
func (n *Node) postMempool(txHash string, tx *types.Transaction) error {
return n.p2pMgr.PubBroadCast(txHash, tx, types.EventTx)
}
//检测是否冗余发送, 或者添加到发送过滤(内部存在直接修改读写保护的数据, 对filter lru的读写需要外层锁保护)
func (n *Node) addIgnoreSendPeerAtomic(filter *utils.Filterdata, key, pid string) (exist bool) {
filter.GetAtomicLock()
defer filter.ReleaseAtomicLock()
var info *sendFilterInfo
if !filter.Contains(key) {
info = &sendFilterInfo{ignoreSendPeers: make(map[string]bool)}
filter.Add(key, info)
} else {
data, _ := filter.Get(key)
info = data.(*sendFilterInfo)
}
_, exist = info.ignoreSendPeers[pid]
info.ignoreSendPeers[pid] = true
return exist
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
import (
"bytes"
"encoding/hex"
"testing"
"time"
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
type versionData struct {
peerName string
rawData interface{}
version int32
}
func Test_processP2P(t *testing.T) {
cfg := types.NewChain33Config(types.ReadFile("../../../chain33.toml"))
q := queue.New("channel")
q.SetConfig(cfg)
go q.Start()
p2p := newP2p(cfg, 12345, "testProcessP2p", q)
defer freeP2p(p2p)
defer q.Close()
node := p2p.node
client := p2p.client
pid := "testPid"
sendChan := make(chan interface{}, 1)
recvChan := make(chan *types.BroadCastData, 1)
testDone := make(chan struct{})
payload := []byte("testpayload")
minerTx := &types.Transaction{Execer: []byte("coins"), Payload: payload, Fee: 14600, Expire: 200}
tx := &types.Transaction{Execer: []byte("coins"), Payload: payload, Fee: 4600, Expire: 2}
tx1 := &types.Transaction{Execer: []byte("coins"), Payload: payload, Fee: 460000000, Expire: 0}
tx2 := &types.Transaction{Execer: []byte("coins"), Payload: payload, Fee: 100, Expire: 1}
txGroup, _ := types.CreateTxGroup([]*types.Transaction{tx1, tx2}, cfg.GetMinTxFeeRate())
gtx := txGroup.Tx()
txList := append([]*types.Transaction{}, minerTx, tx, tx1, tx2)
memTxList := append([]*types.Transaction{}, tx, gtx)
block := &types.Block{
TxHash: []byte("123"),
Height: 10,
Txs: txList,
}
txHash := hex.EncodeToString(tx.Hash())
blockHash := hex.EncodeToString(block.Hash(cfg))
rootHash := merkle.CalcMerkleRoot(cfg, block.Height, txList)
//mempool handler
go func() {
client := q.Client()
client.Sub("mempool")
for msg := range client.Recv() {
switch msg.Ty {
case types.EventTxListByHash:
query := msg.Data.(*types.ReqTxHashList)
var txs []*types.Transaction
if !query.IsShortHash {
txs = memTxList[:1]
} else {
txs = memTxList
}
msg.Reply(client.NewMessage("p2p", types.EventTxListByHash, &types.ReplyTxList{Txs: txs}))
}
}
}()
//测试发送
go func() {
for data := range sendChan {
verData, ok := data.(*versionData)
assert.True(t, ok)
sendData, doSend := node.processSendP2P(verData.rawData, verData.version, verData.peerName, "testIP:port")
txHashFilter.Remove(txHash)
blockHashFilter.Remove(blockHash)
assert.True(t, doSend, "sendData:", verData.rawData)
recvChan <- sendData
}
}()
//测试接收
go func() {
for data := range recvChan {
txHashFilter.Remove(txHash)
blockHashFilter.Remove(blockHash)
handled := node.processRecvP2P(data, pid, node.pubToPeer, "testIP:port")
assert.True(t, handled)
}
}()
go func() {
p2pChan := node.pubsub.Sub("tx")
for data := range p2pChan {
if p2pTx, ok := data.(*types.P2PTx); ok {
sendChan <- &versionData{rawData: p2pTx, version: lightBroadCastVersion}
}
}
}()
//data test
go func() {
subChan := node.pubsub.Sub(pid)
//normal
sendChan <- &versionData{peerName: pid + "1", rawData: &types.P2PTx{Tx: tx, Route: &types.P2PRoute{}}, version: lightBroadCastVersion - 1}
p2p.mgr.PubSub.Pub(client.NewMessage("p2p", types.EventTxBroadcast, tx), P2PTypeName)
sendChan <- &versionData{peerName: pid + "1", rawData: &types.P2PBlock{Block: block}, version: lightBroadCastVersion - 1}
//light broadcast
txHashFilter.Add(hex.EncodeToString(tx1.Hash()), &types.P2PRoute{TTL: DefaultLtTxBroadCastTTL})
p2p.mgr.PubSub.Pub(client.NewMessage("p2p", types.EventTxBroadcast, tx1), P2PTypeName)
sendChan <- &versionData{peerName: pid + "2", rawData: &types.P2PTx{Tx: tx, Route: &types.P2PRoute{TTL: DefaultLtTxBroadCastTTL}}, version: lightBroadCastVersion}
<-subChan //query tx
sendChan <- &versionData{peerName: pid + "2", rawData: &types.P2PBlock{Block: block}, version: lightBroadCastVersion}
<-subChan //query block
for !ltBlockCache.Contains(blockHash) {
}
cpBlock := *ltBlockCache.Get(blockHash).(*types.Block)
assert.True(t, bytes.Equal(rootHash, merkle.CalcMerkleRoot(cfg, cpBlock.Height, cpBlock.Txs)))
//query tx
sendChan <- &versionData{rawData: &types.P2PQueryData{Value: &types.P2PQueryData_TxReq{TxReq: &types.P2PTxReq{TxHash: tx.Hash()}}}}
_, ok := (<-subChan).(*types.P2PTx)
assert.True(t, ok)
sendChan <- &versionData{rawData: &types.P2PQueryData{Value: &types.P2PQueryData_BlockTxReq{BlockTxReq: &types.P2PBlockTxReq{
BlockHash: blockHash,
TxIndices: []int32{1, 2},
}}}}
rep, ok := (<-subChan).(*types.P2PBlockTxReply)
assert.True(t, ok)
assert.Equal(t, 2, int(rep.TxIndices[1]))
sendChan <- &versionData{rawData: &types.P2PQueryData{Value: &types.P2PQueryData_BlockTxReq{BlockTxReq: &types.P2PBlockTxReq{
BlockHash: blockHash,
TxIndices: nil,
}}}}
rep, ok = (<-subChan).(*types.P2PBlockTxReply)
assert.True(t, ok)
assert.Nil(t, rep.TxIndices)
//query reply
sendChan <- &versionData{rawData: &types.P2PBlockTxReply{
BlockHash: blockHash,
TxIndices: []int32{1},
Txs: txList[1:2],
}}
rep1, ok := (<-subChan).(*types.P2PQueryData)
assert.True(t, ok)
assert.Nil(t, rep1.GetBlockTxReq().GetTxIndices())
sendChan <- &versionData{rawData: &types.P2PBlockTxReply{
BlockHash: blockHash,
Txs: txList[0:],
}}
for ltBlockCache.Contains(blockHash) {
}
//max ttl
_, doSend := node.processSendP2P(&types.P2PTx{Tx: tx, Route: &types.P2PRoute{TTL: node.nodeInfo.cfg.MaxTTL + 1}}, lightBroadCastVersion, pid+"5", "testIP:port")
assert.False(t, doSend)
close(testDone)
}()
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
select {
case <-testDone:
return
case <-ticker.C:
t.Error("TestP2PProcessTimeout")
return
}
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gossip
//更新内容:
// 1.p2p 修改为在nat结束后,在启动peer的stream,ping,version 等功能
//2018-3-26 更新内容
// 1. p2p 过滤重复数据,改用blockhash 提换block height
// 2. 增加p2p私钥自动导入到钱包功能
//p2p版本区间 10020, 11000
//历史版本
const (
//p2p广播交易哈希而非完整区块数据
lightBroadCastVersion = 10030
)
// VERSION number
const VERSION = lightBroadCastVersion
// MainNet Channel = 0x0000
const (
defaultTestNetChannel = 256
)
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package init init p2p
package init
import (
_ "github.com/33cn/plugin/plugin/p2p/gossip" //原生gossip p2p插件
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment