github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/pss/client/client.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:44</date> 10 //</624450116379480064> 11 12 13 //+建设!Noclipse,!无协议 14 15 package client 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "sync" 22 "time" 23 24 "github.com/ethereum/go-ethereum/common/hexutil" 25 "github.com/ethereum/go-ethereum/p2p" 26 "github.com/ethereum/go-ethereum/p2p/enode" 27 "github.com/ethereum/go-ethereum/p2p/protocols" 28 "github.com/ethereum/go-ethereum/rlp" 29 "github.com/ethereum/go-ethereum/rpc" 30 "github.com/ethereum/go-ethereum/swarm/log" 31 "github.com/ethereum/go-ethereum/swarm/pss" 32 ) 33 34 const ( 35 handshakeRetryTimeout = 1000 36 handshakeRetryCount = 3 37 ) 38 39 //PSS客户端通过PSS RPC API提供devp2p仿真, 40 //允许从不同的进程访问PSS方法 41 type Client struct { 42 BaseAddrHex string 43 44 //同龄人 45 peerPool map[pss.Topic]map[string]*pssRPCRW 46 protos map[pss.Topic]*p2p.Protocol 47 48 //RPC连接 49 rpc *rpc.Client 50 subs []*rpc.ClientSubscription 51 52 //渠道 53 topicsC chan []byte 54 quitC chan struct{} 55 56 poolMu sync.Mutex 57 } 58 59 //实现p2p.msgreadwriter 60 type pssRPCRW struct { 61 *Client 62 topic string 63 msgC chan []byte 64 addr pss.PssAddress 65 pubKeyId string 66 lastSeen time.Time 67 closed bool 68 } 69 70 func (c *Client) newpssRPCRW(pubkeyid string, addr pss.PssAddress, topicobj pss.Topic) (*pssRPCRW, error) { 71 topic := topicobj.String() 72 err := c.rpc.Call(nil, "pss_setPeerPublicKey", pubkeyid, topic, hexutil.Encode(addr[:])) 73 if err != nil { 74 return nil, fmt.Errorf("setpeer %s %s: %v", topic, pubkeyid, err) 75 } 76 return &pssRPCRW{ 77 Client: c, 78 topic: topic, 79 msgC: make(chan []byte), 80 addr: addr, 81 pubKeyId: pubkeyid, 82 }, nil 83 } 84 85 func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) { 86 msg := <-rw.msgC 87 log.Trace("pssrpcrw read", "msg", msg) 88 pmsg, err := pss.ToP2pMsg(msg) 89 if err != nil { 90 return p2p.Msg{}, err 91 } 92 93 return pmsg, nil 94 } 95 96 //如果只剩下一个信息槽 97 //然后通过握手请求新的 98 //如果缓冲区为空,握手请求将一直阻塞直到返回 99 //在此之后,指针将更改为缓冲区中的第一个新键 100 //如果: 101 //-任何API调用失败 102 //-握手重试在没有回复的情况下耗尽, 103 //-发送失败 104 func (rw *pssRPCRW) WriteMsg(msg p2p.Msg) error { 105 log.Trace("got writemsg pssclient", "msg", msg) 106 if rw.closed { 107 return fmt.Errorf("connection closed") 108 } 109 rlpdata := make([]byte, msg.Size) 110 msg.Payload.Read(rlpdata) 111 pmsg, err := rlp.EncodeToBytes(pss.ProtocolMsg{ 112 Code: msg.Code, 113 Size: msg.Size, 114 Payload: rlpdata, 115 }) 116 if err != nil { 117 return err 118 } 119 120 //拿到钥匙 121 var symkeyids []string 122 err = rw.Client.rpc.Call(&symkeyids, "pss_getHandshakeKeys", rw.pubKeyId, rw.topic, false, true) 123 if err != nil { 124 return err 125 } 126 127 //检查第一把钥匙的容量 128 var symkeycap uint16 129 if len(symkeyids) > 0 { 130 err = rw.Client.rpc.Call(&symkeycap, "pss_getHandshakeKeyCapacity", symkeyids[0]) 131 if err != nil { 132 return err 133 } 134 } 135 136 err = rw.Client.rpc.Call(nil, "pss_sendSym", symkeyids[0], rw.topic, hexutil.Encode(pmsg)) 137 if err != nil { 138 return err 139 } 140 141 //如果这是最后一条有效的消息,则启动新的握手 142 if symkeycap == 1 { 143 var retries int 144 var sync bool 145 //如果它是唯一剩余的密钥,请确保在有新的密钥可供进一步写入之前不会继续。 146 if len(symkeyids) == 1 { 147 sync = true 148 } 149 //开始握手 150 _, err := rw.handshake(retries, sync, false) 151 if err != nil { 152 log.Warn("failing", "err", err) 153 return err 154 } 155 } 156 return nil 157 } 158 159 //握手API调用的重试和同步包装 160 //成功执行后返回第一个新symkeyid 161 func (rw *pssRPCRW) handshake(retries int, sync bool, flush bool) (string, error) { 162 163 var symkeyids []string 164 var i int 165 //请求新密钥 166 //如果密钥缓冲区已耗尽,则将其作为阻塞调用进行,并在放弃之前尝试几次。 167 for i = 0; i < 1+retries; i++ { 168 log.Debug("handshake attempt pssrpcrw", "pubkeyid", rw.pubKeyId, "topic", rw.topic, "sync", sync) 169 err := rw.Client.rpc.Call(&symkeyids, "pss_handshake", rw.pubKeyId, rw.topic, sync, flush) 170 if err == nil { 171 var keyid string 172 if sync { 173 keyid = symkeyids[0] 174 } 175 return keyid, nil 176 } 177 if i-1+retries > 1 { 178 time.Sleep(time.Millisecond * handshakeRetryTimeout) 179 } 180 } 181 182 return "", fmt.Errorf("handshake failed after %d attempts", i) 183 } 184 185 //自定义构造函数 186 // 187 //提供对RPC对象的直接访问 188 func NewClient(rpcurl string) (*Client, error) { 189 rpcclient, err := rpc.Dial(rpcurl) 190 if err != nil { 191 return nil, err 192 } 193 194 client, err := NewClientWithRPC(rpcclient) 195 if err != nil { 196 return nil, err 197 } 198 return client, nil 199 } 200 201 //主要施工单位 202 // 203 //“rpc client”参数允许传递内存中的RPC客户端充当远程WebSocket RPC。 204 func NewClientWithRPC(rpcclient *rpc.Client) (*Client, error) { 205 client := newClient() 206 client.rpc = rpcclient 207 err := client.rpc.Call(&client.BaseAddrHex, "pss_baseAddr") 208 if err != nil { 209 return nil, fmt.Errorf("cannot get pss node baseaddress: %v", err) 210 } 211 return client, nil 212 } 213 214 func newClient() (client *Client) { 215 client = &Client{ 216 quitC: make(chan struct{}), 217 peerPool: make(map[pss.Topic]map[string]*pssRPCRW), 218 protos: make(map[pss.Topic]*p2p.Protocol), 219 } 220 return 221 } 222 223 //在PSS连接上安装新的devp2p protcool 224 // 225 //协议别名为“PSS主题” 226 //使用来自p2p/协议包的普通devp2p发送和传入消息处理程序例程 227 // 228 //当从客户端尚不知道的对等端接收到传入消息时, 229 //这个对等对象被实例化,并且协议在它上面运行。 230 func (c *Client) RunProtocol(ctx context.Context, proto *p2p.Protocol) error { 231 topicobj := pss.BytesToTopic([]byte(fmt.Sprintf("%s:%d", proto.Name, proto.Version))) 232 topichex := topicobj.String() 233 msgC := make(chan pss.APIMsg) 234 c.peerPool[topicobj] = make(map[string]*pssRPCRW) 235 sub, err := c.rpc.Subscribe(ctx, "pss", msgC, "receive", topichex, false, false) 236 if err != nil { 237 return fmt.Errorf("pss event subscription failed: %v", err) 238 } 239 c.subs = append(c.subs, sub) 240 err = c.rpc.Call(nil, "pss_addHandshake", topichex) 241 if err != nil { 242 return fmt.Errorf("pss handshake activation failed: %v", err) 243 } 244 245 //发送传入消息 246 go func() { 247 for { 248 select { 249 case msg := <-msgC: 250 //我们这里只允许sym msgs 251 if msg.Asymmetric { 252 continue 253 } 254 //我们通过了symkeyid 255 //需要symkey本身解析为对等机的pubkey 256 var pubkeyid string 257 err = c.rpc.Call(&pubkeyid, "pss_getHandshakePublicKey", msg.Key) 258 if err != nil || pubkeyid == "" { 259 log.Trace("proto err or no pubkey", "err", err, "symkeyid", msg.Key) 260 continue 261 } 262 //如果我们还没有这个协议上的对等方,请创建它 263 //这或多或少与addpsspeer相同,而不是握手启动 264 if c.peerPool[topicobj][pubkeyid] == nil { 265 var addrhex string 266 err := c.rpc.Call(&addrhex, "pss_getAddress", topichex, false, msg.Key) 267 if err != nil { 268 log.Trace(err.Error()) 269 continue 270 } 271 addrbytes, err := hexutil.Decode(addrhex) 272 if err != nil { 273 log.Trace(err.Error()) 274 break 275 } 276 addr := pss.PssAddress(addrbytes) 277 rw, err := c.newpssRPCRW(pubkeyid, addr, topicobj) 278 if err != nil { 279 break 280 } 281 c.peerPool[topicobj][pubkeyid] = rw 282 p := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%v", addr), []p2p.Cap{}) 283 go proto.Run(p, c.peerPool[topicobj][pubkeyid]) 284 } 285 go func() { 286 c.peerPool[topicobj][pubkeyid].msgC <- msg.Msg 287 }() 288 case <-c.quitC: 289 return 290 } 291 } 292 }() 293 294 c.protos[topicobj] = proto 295 return nil 296 } 297 298 //始终调用此函数以确保我们干净地退出 299 func (c *Client) Close() error { 300 for _, s := range c.subs { 301 s.Unsubscribe() 302 } 303 return nil 304 } 305 306 //添加PSS对等(公钥)并在其上运行协议 307 // 308 //具有匹配主题的client.runprotocol必须 309 //在添加对等机之前运行,否则此方法将 310 //返回一个错误。 311 // 312 //密钥必须存在于PSS节点的密钥存储中 313 //在添加对等机之前。该方法将返回一个错误 314 //如果不是。 315 func (c *Client) AddPssPeer(pubkeyid string, addr []byte, spec *protocols.Spec) error { 316 topic := pss.ProtocolTopic(spec) 317 if c.peerPool[topic] == nil { 318 return errors.New("addpeer on unset topic") 319 } 320 if c.peerPool[topic][pubkeyid] == nil { 321 rw, err := c.newpssRPCRW(pubkeyid, addr, topic) 322 if err != nil { 323 return err 324 } 325 _, err = rw.handshake(handshakeRetryCount, true, true) 326 if err != nil { 327 return err 328 } 329 c.poolMu.Lock() 330 c.peerPool[topic][pubkeyid] = rw 331 c.poolMu.Unlock() 332 p := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%v", addr), []p2p.Cap{}) 333 go c.protos[topic].Run(p, c.peerPool[topic][pubkeyid]) 334 } 335 return nil 336 } 337 338 //删除PSS对等 339 // 340 //TODO:底层清理 341 func (c *Client) RemovePssPeer(pubkeyid string, spec *protocols.Spec) { 342 log.Debug("closing pss client peer", "pubkey", pubkeyid, "protoname", spec.Name, "protoversion", spec.Version) 343 c.poolMu.Lock() 344 defer c.poolMu.Unlock() 345 topic := pss.ProtocolTopic(spec) 346 c.peerPool[topic][pubkeyid].closed = true 347 delete(c.peerPool[topic], pubkeyid) 348 } 349