github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/rlpx.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2015 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package p2p
    26  
    27  import (
    28  	"bytes"
    29  	"crypto/aes"
    30  	"crypto/cipher"
    31  	"crypto/ecdsa"
    32  	"crypto/elliptic"
    33  	"crypto/hmac"
    34  	"crypto/rand"
    35  	"encoding/binary"
    36  	"errors"
    37  	"fmt"
    38  	"hash"
    39  	"io"
    40  	"io/ioutil"
    41  	mrand "math/rand"
    42  	"net"
    43  	"sync"
    44  	"time"
    45  
    46  	"github.com/ethereum/go-ethereum/crypto"
    47  	"github.com/ethereum/go-ethereum/crypto/ecies"
    48  	"github.com/ethereum/go-ethereum/crypto/secp256k1"
    49  	"github.com/ethereum/go-ethereum/crypto/sha3"
    50  	"github.com/ethereum/go-ethereum/p2p/discover"
    51  	"github.com/ethereum/go-ethereum/rlp"
    52  	"github.com/golang/snappy"
    53  )
    54  
    55  const (
    56  	maxUint24 = ^uint32(0) >> 8
    57  
    58  sskLen = 16 //指定最大共享密钥长度(pubkey)/2
    59  sigLen = 65 //椭圆形S256
    60  pubLen = 64 //512位pubkey,未压缩表示,无格式字节
    61  shaLen = 32 //哈希长度(用于nonce等)
    62  
    63  	authMsgLen  = sigLen + shaLen + pubLen + shaLen + 1
    64  	authRespLen = pubLen + shaLen + 1
    65  
    66   /*eSoverhead=65/*pubkey*/+16/*iv*/+32/*mac*/
    67  
    68   encauthmsglen=authmsglen+eciesoverhead//加密的pre-eip-8发起程序握手大小
    69   encauthresplen=authresplen+eciesoverhead//加密的pre-eip-8握手回复的大小
    70  
    71   //加密握手和协议的总超时时间
    72   //双向握手。
    73   握手超时=5*次。秒
    74  
    75   //这是发送断开连接原因的超时。
    76   //这比通常的超时时间短,因为我们不希望
    77   //等待连接是否损坏。
    78   discWriteTimeout=1*时间。秒
    79  )
    80  
    81  //如果解压缩的消息长度超过
    82  //允许的24位(即长度大于等于16MB)。
    83  var errplanmessagetoolarge=errors.new(“消息长度>=16MB”)
    84  
    85  //rlpx是实际(非测试)连接使用的传输协议。
    86  //它用锁和读/写截止时间包装帧编码器。
    87  RLPX型结构
    88   康德网络
    89  
    90   rmu,wmu同步.mutex
    91   rw*rlpxframerw
    92  }
    93  
    94  func newrlpx(fd net.conn)传输
    95   fd.setDeadline(time.now().add(handshakeTimeout))。
    96   返回&rlpx fd:fd
    97  }
    98  
    99  func(t*rlpx)readmsg()(msg,错误)
   100   锁定()
   101   延迟t.rmu.unlock()
   102   t.fd.setreadDeadline(time.now().add(framereadTimeout))。
   103   返回t.rw.readmsg()。
   104  }
   105  
   106  func(t*rlpx)writemsg(msg msg)错误
   107   T.WMU.
   108   延迟t.wmu.unlock()
   109   t.fd.setWriteDeadline(time.now().add(frameWriteTimeout))。
   110   返回t.rw.writemsg(msg)
   111  }
   112  
   113  func(t*rlpx)关闭(err错误)
   114   T.WMU.
   115   延迟t.wmu.unlock()
   116   //如果可能,告诉远端为什么要断开连接。
   117   如果T.RW!= nIL{
   118    如果R,则OK:=错误(不合理);OK&&R!=DiscNetworkError
   119     //rlpx尝试向断开连接的对等端发送discreason
   120     //如果连接为net.pipe(内存模拟)
   121     //它永远挂起,因为net.pipe不实现
   122     //写入截止时间。因为这只会试图发送
   123     //如果没有错误,则显示断开原因消息。
   124     if err:=t.fd.setWriteDeadline(time.now().add(discWriteTimeout));err==nil
   125      发送项(t.rw、discmsg、r)
   126     }
   127    }
   128   }
   129   T.F.C.()
   130  }
   131  
   132  func(t*rlpx)doprotochandshake(our*protochandshake)(their*protochandshake,err error)
   133   //写我们的握手是同时进行的,我们更喜欢
   134   //返回握手读取错误。如果远端
   135   //有正当理由提前断开我们的连接,我们应该将其返回
   136   //作为错误,以便在其他地方跟踪它。
   137   werr:=make(chan错误,1)
   138   go func()werr<-send(t.rw,handshakemsg,our)()
   139   如果他们的,err=readProtocolHandshake(t.rw,我们的);err!= nIL{
   140    <-werr//确保写入也终止
   141    返回零
   142   }
   143   如果错误:=<-werr;err!= nIL{
   144    返回nil,fmt.errorf(“写入错误:%v”,err)
   145   }
   146   //如果协议版本支持快速编码,则立即升级
   147   t.rw.snappy=their.version>=snappyrotocolversion
   148  
   149   归还他们的,零
   150  }
   151  
   152  func readprotocolhandshake(rw msgreader,our*protochandshake)(*protochandshake,error)
   153   msg,err:=rw.readmsg()。
   154   如果犯错!= nIL{
   155    返回零
   156   }
   157   如果消息大小>baseprotocolmaxmsgsize
   158    返回nil,fmt.errorf(“消息太大”)
   159   }
   160   如果msg.code==discmsg
   161    //根据协议握手有效之前断开连接
   162    //spec,如果posthashake检查失败,我们将自己发送它。
   163    //但是我们不能直接返回原因,因为它被回送了
   164    //否则返回。改为用绳子把它包起来。
   165    VaR原因[1]不一致
   166    rlp.decode(消息有效负载和原因)
   167    返回零,原因[0]
   168   }
   169   如果是MSG,代码!=握手
   170    返回nil,fmt.errorf(“预期握手,得到%x”,消息代码)
   171   }
   172   var-hs协议握手
   173   如果错误:=msg.decode(&hs);错误!= nIL{
   174    返回零
   175   }
   176   if(hs.id==discover.nodeid)
   177    返回零,DiscInvalidIdentity
   178   }
   179   返回与HS
   180  }
   181  
   182  //doenchandshake使用authenticated运行协议握手
   183  / /消息。协议握手是第一条经过身份验证的消息
   184  //并验证加密握手是否“有效”以及
   185  //远程端实际上提供了正确的公钥。
   186  func(t*rlpx)doenchandshake(prv*ecdsa.privatekey,dial*discover.node)(discover.nodeid,错误)
   187   var
   188    证券交易秘密
   189    错误率
   190   )
   191   如果刻度盘=零
   192    sec,err=receiverenchandshake(t.fd,prv)
   193   }否则{
   194    sec,err=initiatorenchandshake(t.fd,prv,dial.id)
   195   }
   196   如果犯错!= nIL{
   197    返回discover.nodeid,err
   198   }
   199   T.WMU.
   200   t.rw=newrlpxframerw(t.fd,秒)
   201   解锁()
   202   返回sec.remoteid,零
   203  }
   204  
   205  //enchandshake包含加密握手的状态。
   206  类型enchandshake结构
   207   引发剂布尔
   208   remoteid发现.nodeid
   209  
   210   remotepub*ecies.publickey//远程pubk
   211   initnoce,respnoce[]字节//nonce
   212   randomprivkey*ecies.privatekey//ecdhe随机
   213   remoterandompub*ecies.publickey//ecdhe random pubk
   214  }
   215  
   216  //机密表示连接机密
   217  //在加密握手期间协商。
   218  类型机密结构
   219   remoteid发现.nodeid
   220   aes,mac[]字节
   221   出口MAC,入口MAC哈希.hash
   222   标记[]字节
   223  }
   224  
   225  //rlpx v4握手认证(在eip-8中定义)。
   226  类型authmsgv4结构
   227   gotplan bool//读包是否有纯格式。
   228  
   229   签名[siglen]字节
   230   initiatorSubkey[publen]字节
   231   nonce[shalen]字节
   232   版本uint
   233  
   234   //忽略其他字段(向前兼容)
   235   其余[]rlp.rawvalue`rlp:“tail”`
   236  }
   237  
   238  //rlpx v4握手响应(在eip-8中定义)。
   239  键入authrespv4 struct_
   240   randompubkey[publen]字节
   241   nonce[shalen]字节
   242   版本uint
   243  
   244   //忽略其他字段(向前兼容)
   245   其余[]rlp.rawvalue`rlp:“tail”`
   246  }
   247  
   248  //在握手完成后调用secrets。
   249  //从握手值中提取连接机密。
   250  func(h*enchandshake)机密(auth,authresp[]byte)(机密,错误)
   251   ecdhesecret,err:=h.randomprivkey.generateshared(h.remoterrandompub,ssklen,ssklen)
   252   如果犯错!= nIL{
   253    返回机密,错误
   254   }
   255  
   256   //从临时密钥协议派生基本机密
   257   sharedSecret:=crypto.keccak256(ecdhesecret,crypto.keccak256(h.responce,h.initonce))。
   258   aesecret:=crypto.keccak256(ecdhesecret,sharedsecret)
   259   S: =秘密{
   260    远程ID:h.remoteID,
   261    伊丝:伊丝密,
   262    mac:crypto.keccak256(ecdhesecret,aesecret)
   263   }
   264  
   265   //为macs设置sha3实例
   266   mac1:=sha3.newkeccak256()。
   267   mac1.write(xor(s.mac,h.respnce))。
   268   mac1.写入(auth)
   269   mac2:=sha3.newkeccak256()。
   270   mac2.write(xor(s.mac,h.initonce))。
   271   MAC2.写入(authresp)
   272   如果H.发起人
   273    S.egressmac,S.ingressmac=mac1,mac2
   274   }否则{
   275    S.egressmac,S.ingressmac=mac2,mac1
   276   }
   277  
   278   返回零
   279  }
   280  
   281  //StaticSharedSecret返回静态共享机密,结果
   282  //本地和远程静态节点密钥之间的密钥协议。
   283  func(h*enchandshake)staticsharedsecret(prv*ecdsa.privatekey)([]字节,错误)
   284   返回ecies.importecdsa(prv).generateshared(h.remotepub、ssklen、ssklen)
   285  }
   286  
   287  //initiatorenchandshake在conn上协商会话令牌。
   288  //应该在连接的拨号端调用它。
   289  / /
   290  //prv是本地客户端的私钥。
   291  func initiatorenchandshake(conn io.readwriter,prv*ecdsa.privatekey,remoteid discover.nodeid)(s secrets,err error)
   292   h:=&enchandshake发起方:真,远程ID:远程ID
   293   authmsg,错误:=h.makeauthmsg(prv)
   294   如果犯错!= nIL{
   295    返回S
   296   }
   297   authpacket,错误:=sealeip8(authmsg,h)
   298   如果犯错!= nIL{
   299    返回S
   300   }
   301   如果,err=conn.write(authpacket);err!= nIL{
   302    返回S
   303   }
   304  
   305   AuthRespMsg:=新建(AuthRespv4)
   306   AuthRespPacket,错误:=readHandshakemsg(AuthRespMsg,EnauthRespLen,prv,conn)
   307   如果犯错!= nIL{
   308    返回S
   309   }
   310   如果错误:=H.HandleAuthResp(AuthRespMsg);错误!= nIL{
   311    返回S
   312   }
   313   返回h.secrets(authpacket、authrespacket)
   314  }
   315  
   316  //makeauthmsg创建启动器握手消息。
   317  func(h*enchandshake)makeauthmsg(prv*ecdsa.privatekey)(*authmsgv4,错误)
   318   rpub,错误:=h.remoteid.pubkey()
   319   如果犯错!= nIL{
   320    返回nil,fmt.errorf(“错误的remoteid:%v”,err)
   321   }
   322   h.remotepub=ecies.importecdsapublic(rpub)
   323   //生成随机发起程序nonce。
   324   h.initonce=生成([]字节,shalen)
   325   如果,错误:=rand.read(h.initnoce);错误!= nIL{
   326    返回零
   327   }
   328   //为ECDH生成随机密钥对to。
   329   h.randomprivkey,err=ecies.generatekey(rand.reader,crypto.s256(),nil)
   330   如果犯错!= nIL{
   331    返回零
   332   }
   333  
   334   //为已知消息签名:静态共享机密^nonce
   335   令牌,错误:=h.staticsharedsecret(prv)
   336   如果犯错!= nIL{
   337    返回零
   338   }
   339   有符号:=xor(token,h.initonce)
   340   签名,错误:=crypto.sign(signed,h.randomprivkey.exportecdsa())
   341   如果犯错!= nIL{
   342    返回零
   343   }
   344  
   345   消息:=new(authmsgv4)
   346   副本(消息签名[:],签名)
   347   复制(msg.initiatorpubkey[:],crypto.fromecdsapub(&prv.publickey)[1:]
   348   复制(msg.nonce[:],h.initonce)
   349   版本=4
   350   返回MSG,NIL
   351  }
   352  
   353  func(h*enchandshake)handleauthresp(msg*authresp4)(错误)
   354   h.respnce=msg.nonce[:]
   355   h.remoterandompub,err=importpublickey(msg.randompubkey[:])
   356   返回错误
   357  }
   358  
   359  //receiverenchandshake在conn上协商会话令牌。
   360  //应该在连接的侦听端调用它。
   361  / /
   362  //prv是本地客户端的私钥。
   363  func receiverenchandshake(conn io.readwriter,prv*ecdsa.privatekey)(s secrets,err error)
   364   authmsg:=新建(authmsgv4)
   365   authpacket,错误:=readhandshakemsg(authmsg,encauthmsglen,prv,conn)
   366   如果犯错!= nIL{
   367    返回S
   368   }
   369   H:=新(Enchandshake)
   370   如果错误:=h.handleauthmsg(authmsg,prv);错误!= nIL{
   371    返回S
   372   }
   373  
   374   AuthRespMsg,错误:=H.MakeAuthResp()。
   375   如果犯错!= nIL{
   376    返回S
   377   }
   378   var authresppacket[]字节
   379   如果authmsg.gotplan
   380    AuthRespPacket,err=AuthRespMsg.SealPlain(H)
   381   }否则{
   382    AuthRespPacket,err=SealeIP8(AuthRespMsg,H)
   383   }
   384   如果犯错!= nIL{
   385    返回S
   386   }
   387   如果,err=conn.write(authresppacket);err!= nIL{
   388    返回S
   389   }
   390   返回h.secrets(authpacket、authrespacket)
   391  }
   392  
   393  func(h*enchandshake)handleauthmsg(msg*authmsgv4,prv*ecdsa.privatekey)错误
   394   //导入远程标识。
   395   h.initonce=msg.nonce[:]
   396   h.remoteid=msg.initiatorSubkey
   397   rpub,错误:=h.remoteid.pubkey()
   398   如果犯错!= nIL{
   399    返回fmt.errorf(“错误的remoteid:%v”,err)
   400   }
   401   h.remotepub=ecies.importecdsapublic(rpub)
   402  
   403   //为ECDH生成随机密钥对。
   404   //如果已经设置了私钥,则使用它而不是生成一个(用于测试)。
   405   如果h.randomprivkey==nil
   406    h.randomprivkey,err=ecies.generatekey(rand.reader,crypto.s256(),nil)
   407    如果犯错!= nIL{
   408     返回错误
   409    }
   410   }
   411  
   412   //检查签名。
   413   令牌,错误:=h.staticsharedsecret(prv)
   414   如果犯错!= nIL{
   415    返回错误
   416   }
   417   signedsg:=xor(token,h.initonce)
   418   remoteRandoPub,错误:=secp256k1.recoverpubkey(signedsg,msg.signature[:])
   419   如果犯错!= nIL{
   420    返回错误
   421   }
   422   H.RemoteRandompub,u=导入公共密钥(RemoteRandompub)
   423   返回零
   424  }
   425  
   426  func(h*enchandshake)makeauthresp()(msg*authresp4,err error)
   427   //生成随机nonce。
   428   h.responce=make([]字节,shalen)
   429   如果,err=rand.read(h.respnce);err!= nIL{
   430    返回零
   431   }
   432  
   433   msg=新建(authrespv4)
   434   复制(msg.nonce[:],h.respnce)
   435   复制(msg.randompubkey[:],exportpubkey(&h.randomprivkey.publickey))
   436   版本=4
   437   返回MSG,NIL
   438  }
   439  
   440  func(msg*authmsgv4)sealplain(h*enchandshake)([]字节,错误)
   441   buf:=make([]字节,authmsglen)
   442   n:=副本(buf,msg.签名[:])
   443   n+=复制(buf[n:],crypto.keccak256(exportpubkey(&h.randomprivkey.publickey)))
   444   n+=复制(buf[n:],msg.initiatorSubkey[:])
   445   n+=复制(buf[n:],msg.nonce[:])
   446   buf[n]=0//令牌标志
   447   返回ecies.encrypt(rand.reader,h.remotepub,buf,nil,nil)
   448  }
   449  
   450  func(msg*authmsgv4)decodeplain(input[]byte)
   451   n:=复制(消息签名[:],输入)
   452   n+=shalen//跳过sha3(发起人短暂发布)
   453   n+=复制(msg.initiatorSubkey[:],输入[n:]
   454   复制(msg.nonce[:],输入[n:])
   455   版本=4
   456   msg.gotplan=真
   457  }
   458  
   459  func(msg*authrespv4)sealplain(hs*enchandshake)([]字节,错误)
   460   buf:=make([]字节,authresplen)
   461   n:=复制(buf,msg.randombkey[:])
   462   复制(buf[n:],msg.nonce[:])
   463   返回ecies.encrypt(rand.reader、hs.remotepub、buf、nil、nil)
   464  }
   465  
   466  func(msg*authrespv4)decodeplain(input[]byte)
   467   n:=复制(msg.randompubkey[:],输入)
   468   复制(msg.nonce[:],输入[n:])
   469   版本=4
   470  }
   471  
   472  var padspace=make([]字节,300)
   473  
   474  func sealeip8(msg interface,h*enchandshake)([]字节,错误)
   475   buf:=新建(bytes.buffer)
   476   如果错误:=rlp.encode(buf,msg);错误!= nIL{
   477    返回零
   478   }
   479   //用随机数据量填充。数量至少需要100个字节才能
   480   //该消息可与EIP-8之前的握手区分开来。
   481   焊盘:=padspace[:mrand.intn(len(padspace)-100)+100]
   482   BUF.写(PAD)
   483   前缀:=make([]字节,2)
   484   binary.bigendian.putuint16(前缀,uint16(buf.len()+eciesoverhead))
   485  
   486   enc,err:=ecies.encrypt(rand.reader,h.remotepub,buf.bytes(),nil,前缀)
   487   返回append(prefix,enc…),err
   488  }
   489  
   490  类型普通解码器接口
   491   decodeplain([]字节)
   492  }
   493  
   494  func readhandshakemsg(msg plaindecoder,plainsize int,prv*ecdsa.privatekey,r io.reader)([]字节,错误)
   495   buf:=make([]字节,普通大小)
   496   如果,错误:=io.readfull(r,buf);错误!= nIL{
   497    返回BUF
   498   }
   499   //尝试解码pre-eip-8“plain”格式。
   500   键:=ecies.importecdsa(prv)
   501   如果是dec,则错误:=key.decrypt(buf,nil,nil);err==nil
   502    解码原稿消息(DEC)
   503    返回BUF,NIL
   504   }
   505   //可以是EIP-8格式,试试看。
   506   前缀:=buf[:2]
   507   大小:=binary.bigendian.uint16(前缀)
   508   如果尺寸<uint16(plainsize)
   509    返回buf,fmt.errorf(“大小下溢,至少需要%d字节”,plainsize)
   510   }
   511   buf=append(buf,make([]byte,size-uint16(plainsize)+2)…)
   512   如果uu,错误:=io.readfull(r,buf[plainsize:]);错误!= nIL{
   513    返回BUF
   514   }
   515   dec,err:=key.decrypt(buf[2:],nil,前缀)
   516   如果犯错!= nIL{
   517    返回BUF
   518   }
   519   //此处不能使用rlp.decodebytes,因为它拒绝
   520   //尾随数据(向前兼容)。
   521   S:=rlp.newstream(bytes.newreader(dec),0)
   522   返回buf,s.decode(msg)
   523  }
   524  
   525  //importpublickey取消标记512位公钥。
   526  func importpublickey(pubkey[]byte)(*ecies.publickey,error)
   527   var pubkey65[]字节
   528   交换长度(pubkey)
   529   案例64:
   530    //添加“uncompressed key”标志
   531    pubkey65=append([]字节0x04,pubkey…)
   532   案例65:
   533    pubKey65=发布键
   534   违约:
   535    返回nil,fmt.errorf(“无效的公钥长度%v(预期64/65)”,len(pubkey))
   536   }
   537   //TODO:更少的无意义转换
   538   pub,err:=crypto.unmashalpubkey(pubkey65)
   539   如果犯错!= nIL{
   540    返回零
   541   }
   542   退货特定进口适用性(pub),无
   543  }
   544  
   545  func exportpubkey(pub*ecies.publickey)[]字节
   546   如果Pub=nI{
   547    恐慌(“nil pubkey”)
   548   }
   549   返回Elliptic.Marshal(pub.curve,pub.x,pub.y)[1:]
   550  }
   551  
   552  func xor(一个,另一个[]字节)(xor[]字节)
   553   xor=make([]byte,len(one))。
   554   对于i:=0;i<len(one);i++
   555    xor[i]=一个[i]^另一个[i]
   556   }
   557   返回异或
   558  }
   559  
   560  var
   561   //用于代替实际帧头数据。
   562   //TODO:当msg包含协议类型代码时替换此项。
   563   ZeroHeader=[]字节0xc2、0x80、0x80_
   564   //十六个零字节
   565   zero16=make([]字节,16)
   566  )
   567  
   568  //rlpxframerw实现了rlpx framework的简化版本。
   569  //不支持分块消息,所有头等于
   570  //ZooHead。
   571  / /
   572  //rlpxframerw对于从多个goroutine并发使用不安全。
   573  类型rlpxframerw struct_
   574   连接IO.readwriter
   575   加密流
   576   DEC密码流
   577  
   578   maccipher cipher.block
   579   Egressmac哈希.hash
   580   入口MAC哈希.hash
   581  
   582   快活布尔
   583  }
   584  
   585  func newrlpxframerw(conn io.readwriter,s secrets)*rlpxframerw_
   586   macc,err:=aes.newcipher(s.mac)
   587   如果犯错!= nIL{
   588    panic(“无效的MAC机密:”+err.error())
   589   }
   590   ENCC,错误:=aes.newcipher(s.aes)
   591   如果犯错!= nIL{
   592    panic(“无效的aes秘密:”+err.error())
   593   }
   594   //我们对aes使用全零IV,因为使用的密钥
   595   //因为加密是短暂的。
   596   iv:=make([]字节,encc.blocksize())
   597   返回&rlpxframerw_
   598    连接件:连接件,
   599    Enc:Cipher.Newctr(Encc,IV),第
   600    DEC:cipher.newctr(encc,iv)
   601    麦克西弗:麦克西弗,
   602    白鹭:白鹭,
   603    入口MAC:S.IngressMAC,
   604   }
   605  }
   606  
   607  func(rw*rlpxframerw)writemsg(msg msg)错误
   608   ptype,:=rlp.encodetobytes(消息代码)
   609  
   610   //如果启用了snappy,则立即压缩消息
   611   如果RW.SNAPPY {
   612    如果消息大小>maxuint24
   613     返回errplanmessagetoolarge
   614    }
   615    有效载荷,:=ioutil.readall(msg.payload)
   616    有效载荷=快速编码(零,有效载荷)
   617  
   618    msg.payload=bytes.newreader(有效负载)
   619    msg.size=uint32(len(有效载荷)
   620   }
   621   //写头
   622   headbuf:=make([]字节,32)
   623   fsize:=uint32(len(ptype))+消息大小
   624   如果fsize>maxuint24
   625    返回errors.new(“消息大小溢出uint24”)
   626   }
   627   putint24(fsize,headbuf)//todo:检查溢出
   628   复制(headbuf[3:],zeroheader)
   629   rw.enc.xorkeystream(headbuf[:16],headbuf[:16])//上半部分现在已加密
   630  
   631   //写入头MAC
   632   副本(headbuf[16:],updatemac(rw.egissmac,rw.maccipher,headbuf[:16]))
   633   如果uux,错误:=rw.conn.write(headbuf);错误!= nIL{
   634    返回错误
   635   }
   636  
   637   //写入加密帧,更新出口MAC哈希
   638   //写入conn的数据。
   639   tee:=cipher.streamwriter s:rw.enc,w:io.multiwriter(rw.conn,rw.egissmac)
   640   如果ux,err:=tee.write(ptype);err!= nIL{
   641    返回错误
   642   }
   643   如果uu,错误:=io.copy(tee,msg.payload);错误!= nIL{
   644    返回错误
   645   }
   646   如果填充:=fsize%16;填充>0
   647    如果u,err:=tee.write(zero16[:16 padding]);err!= nIL{
   648     返回错误
   649    }
   650   }
   651  
   652   //写入帧mac。出口MAC哈希是最新的,因为
   653   //框架内容也写入了它。
   654   fmacseed:=rw.egissmac.sum(零)
   655   mac:=更新mac(rw.egissmac、rw.maccipher、fmacseed)
   656   _uuErr:=rw.conn.write(mac)
   657   返回错误
   658  }
   659  
   660  func(rw*rlpxframerw)readmsg()(msg,err error)
   661   //读取头
   662   headbuf:=make([]字节,32)
   663   如果,错误:=io.readfull(rw.conn,headbuf);错误!= nIL{
   664    返回MSG
   665   }
   666   //验证头MAC
   667   shouldmac:=updateMac(rw.ingressmac,rw.maccipher,headbuf[:16])
   668   如果!hmac.相等(shouldmac,headbuf[16:)
   669    返回msg,errors.new(“错误的header mac”)
   670   }
   671   rw.dec.xorkeystream(headbuf[:16],headbuf[:16])//上半部分现在被解密
   672   fsize:=readInt24(headbuf)
   673   //暂时忽略协议类型
   674  
   675   //读取帧内容
   676   var rsize=fsize//帧大小四舍五入到16字节边界
   677   如果填充:=fsize%16;填充>0
   678    rsize+=16-填充
   679   }
   680   framebuf:=make([]字节,rsize)
   681   如果uux,错误:=io.readfull(rw.conn,framebuf);错误!= nIL{
   682    返回MSG
   683   }
   684  
   685   //读取并验证帧MAC。我们可以重复使用headbuf。
   686   rw.ingressmac.write(framebuf)
   687   fmacseed:=rw.ingressmac.sum(零)
   688   如果uu,错误:=io.readfull(rw.conn,headbuf[:16]);错误!= nIL{
   689    返回MSG
   690   }
   691   shouldmac=updateMac(rw.ingressmac,rw.maccipher,fmacseed)
   692   如果!hmac.equal(shouldmac,headbuf[:16]);
   693    返回msg,errors.new(“坏帧MAC”)
   694   }
   695  
   696   //解密帧内容
   697   rw.dec.xorkeystream(framebuf,framebuf)代码
   698  
   699   //解码消息代码
   700   内容:=bytes.newreader(framebuf[:fsize])
   701   如果错误:=rlp.decode(content,&msg.code);错误!= nIL{
   702    返回MSG
   703   }
   704   msg.size=uint32(content.len())
   705   msg.payload=内容
   706  
   707   //如果启用了snappy,则验证并解压缩消息
   708   如果RW.SNAPPY {
   709    有效负载,错误:=ioutil.readall(msg.payload)
   710    如果犯错!= nIL{
   711     返回MSG
   712    }
   713    大小,错误:=snappy.decodedlen(有效负载)
   714    如果犯错!= nIL{
   715     返回MSG
   716    }
   717    如果大小>int(maxuint24)
   718     返回消息,errplanmessagetoolarge
   719    }
   720    有效载荷,err=snappy.decode(nil,有效载荷)
   721    如果犯错!= nIL{
   722     返回MSG
   723    }
   724    msg.size,msg.payload=uint32(大小),bytes.newreader(有效负载)
   725   }
   726   返回MSG,NIL
   727  }
   728  
   729  //updateMac使用加密种子重新设置给定哈希。
   730  //返回种子设定后哈希和的前16个字节。
   731  func updatemac(mac hash.hash,block cipher.block,seed[]byte)[]byte_
   732   aesbuf:=make([]字节,aes.blocksize)
   733   block.encrypt(aesbuf,mac.sum(nil))。
   734   对于i:=范围aesbuf
   735    aesbuf[i]^=种子[i]
   736   }
   737   书写(aesbuf)
   738   返回mac.sum(nil)[:16]
   739  }
   740  
   741  func readint24(b[]字节)uint32
   742   返回uint32(b[2])uint32(b[1])<<8_uint32(b[0])<<16
   743  }
   744  
   745  func putint24(v uint32,b[]字节)
   746   B[0]=字节(V>>16)
   747   B[1]=字节(V>>8)
   748   B〔2〕=字节(V)
   749  }