github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/pss/handshake.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  //</624450116601778176>
    11  
    12  
    13  //+建设!诺普桑德摇晃
    14  
    15  package pss
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/common/hexutil"
    26  	"github.com/ethereum/go-ethereum/crypto"
    27  	"github.com/ethereum/go-ethereum/p2p"
    28  	"github.com/ethereum/go-ethereum/rlp"
    29  	"github.com/ethereum/go-ethereum/rpc"
    30  	"github.com/ethereum/go-ethereum/swarm/log"
    31  )
    32  
    33  const (
    34  	IsActiveHandshake = true
    35  )
    36  
    37  var (
    38  	ctrlSingleton *HandshakeController
    39  )
    40  
    41  const (
    42  defaultSymKeyRequestTimeout = 1000 * 8  //接收对握手符号请求的响应的最大等待毫秒数
    43  defaultSymKeyExpiryTimeout  = 1000 * 10 //ms等待,然后允许垃圾收集过期的symkey
    44  defaultSymKeySendLimit      = 256       //symkey的有效消息量
    45  defaultSymKeyCapacity       = 4         //同时存储/发送的最大符号键数
    46  )
    47  
    48  //对称密钥交换消息负载
    49  type handshakeMsg struct {
    50  	From    []byte
    51  	Limit   uint16
    52  	Keys    [][]byte
    53  	Request uint8
    54  	Topic   Topic
    55  }
    56  
    57  //单个对称密钥的内部表示法
    58  type handshakeKey struct {
    59  	symKeyID  *string
    60  	pubKeyID  *string
    61  	limit     uint16
    62  	count     uint16
    63  	expiredAt time.Time
    64  }
    65  
    66  //所有输入和输出密钥的容器
    67  //对于一个特定的对等(公钥)和主题
    68  type handshake struct {
    69  	outKeys []handshakeKey
    70  	inKeys  []handshakeKey
    71  }
    72  
    73  //握手控制器的初始化参数
    74  //
    75  //symkeyrequestexpiry:等待握手回复超时
    76  //(默认8000 ms)
    77  //
    78  //symkeysendlimit:对称密钥问题的消息量
    79  //此节点的有效期为(默认256)
    80  //
    81  //symkeycapacity:对称密钥的理想(和最大)数量
    82  //每个对等端的每个方向保持(默认为4)
    83  type HandshakeParams struct {
    84  	SymKeyRequestTimeout time.Duration
    85  	SymKeyExpiryTimeout  time.Duration
    86  	SymKeySendLimit      uint16
    87  	SymKeyCapacity       uint8
    88  }
    89  
    90  //握手控制器初始化的正常默认值
    91  func NewHandshakeParams() *HandshakeParams {
    92  	return &HandshakeParams{
    93  		SymKeyRequestTimeout: defaultSymKeyRequestTimeout * time.Millisecond,
    94  		SymKeyExpiryTimeout:  defaultSymKeyExpiryTimeout * time.Millisecond,
    95  		SymKeySendLimit:      defaultSymKeySendLimit,
    96  		SymKeyCapacity:       defaultSymKeyCapacity,
    97  	}
    98  }
    99  
   100  //启用半自动diffie-hellman的singleton对象
   101  //交换短暂对称密钥
   102  type HandshakeController struct {
   103  	pss                  *Pss
   104  keyC                 map[string]chan []string //添加握手成功时要报告的频道
   105  	lock                 sync.Mutex
   106  	symKeyRequestTimeout time.Duration
   107  	symKeyExpiryTimeout  time.Duration
   108  	symKeySendLimit      uint16
   109  	symKeyCapacity       uint8
   110  	symKeyIndex          map[string]*handshakeKey
   111  	handshakes           map[string]map[Topic]*handshake
   112  	deregisterFuncs      map[Topic]func()
   113  }
   114  
   115  //将握手控制器连接到PSS节点
   116  //
   117  //必须在启动PSS节点服务之前调用
   118  func SetHandshakeController(pss *Pss, params *HandshakeParams) error {
   119  	ctrl := &HandshakeController{
   120  		pss:                  pss,
   121  		keyC:                 make(map[string]chan []string),
   122  		symKeyRequestTimeout: params.SymKeyRequestTimeout,
   123  		symKeyExpiryTimeout:  params.SymKeyExpiryTimeout,
   124  		symKeySendLimit:      params.SymKeySendLimit,
   125  		symKeyCapacity:       params.SymKeyCapacity,
   126  		symKeyIndex:          make(map[string]*handshakeKey),
   127  		handshakes:           make(map[string]map[Topic]*handshake),
   128  		deregisterFuncs:      make(map[Topic]func()),
   129  	}
   130  	api := &HandshakeAPI{
   131  		namespace: "pss",
   132  		ctrl:      ctrl,
   133  	}
   134  	pss.addAPI(rpc.API{
   135  		Namespace: api.namespace,
   136  		Version:   "0.2",
   137  		Service:   api,
   138  		Public:    true,
   139  	})
   140  	ctrlSingleton = ctrl
   141  	return nil
   142  }
   143  
   144  //返回存储区中所有未过期的对称密钥
   145  //对等(公钥)、主题和指定方向
   146  func (ctl *HandshakeController) validKeys(pubkeyid string, topic *Topic, in bool) (validkeys []*string) {
   147  	ctl.lock.Lock()
   148  	defer ctl.lock.Unlock()
   149  	now := time.Now()
   150  	if _, ok := ctl.handshakes[pubkeyid]; !ok {
   151  		return []*string{}
   152  	} else if _, ok := ctl.handshakes[pubkeyid][*topic]; !ok {
   153  		return []*string{}
   154  	}
   155  	var keystore *[]handshakeKey
   156  	if in {
   157  		keystore = &(ctl.handshakes[pubkeyid][*topic].inKeys)
   158  	} else {
   159  		keystore = &(ctl.handshakes[pubkeyid][*topic].outKeys)
   160  	}
   161  
   162  	for _, key := range *keystore {
   163  		if key.limit <= key.count {
   164  			ctl.releaseKey(*key.symKeyID, topic)
   165  		} else if !key.expiredAt.IsZero() && key.expiredAt.Before(now) {
   166  			ctl.releaseKey(*key.symKeyID, topic)
   167  		} else {
   168  			validkeys = append(validkeys, key.symKeyID)
   169  		}
   170  	}
   171  	return
   172  }
   173  
   174  //将具有有效性限制的所有给定对称密钥添加到存储方式
   175  //对等(公钥)、主题和指定方向
   176  func (ctl *HandshakeController) updateKeys(pubkeyid string, topic *Topic, in bool, symkeyids []string, limit uint16) {
   177  	ctl.lock.Lock()
   178  	defer ctl.lock.Unlock()
   179  	if _, ok := ctl.handshakes[pubkeyid]; !ok {
   180  		ctl.handshakes[pubkeyid] = make(map[Topic]*handshake)
   181  
   182  	}
   183  	if ctl.handshakes[pubkeyid][*topic] == nil {
   184  		ctl.handshakes[pubkeyid][*topic] = &handshake{}
   185  	}
   186  	var keystore *[]handshakeKey
   187  	expire := time.Now()
   188  	if in {
   189  		keystore = &(ctl.handshakes[pubkeyid][*topic].inKeys)
   190  	} else {
   191  		keystore = &(ctl.handshakes[pubkeyid][*topic].outKeys)
   192  		expire = expire.Add(time.Millisecond * ctl.symKeyExpiryTimeout)
   193  	}
   194  	for _, storekey := range *keystore {
   195  		storekey.expiredAt = expire
   196  	}
   197  	for i := 0; i < len(symkeyids); i++ {
   198  		storekey := handshakeKey{
   199  			symKeyID: &symkeyids[i],
   200  			pubKeyID: &pubkeyid,
   201  			limit:    limit,
   202  		}
   203  		*keystore = append(*keystore, storekey)
   204  		ctl.pss.symKeyPool[*storekey.symKeyID][*topic].protected = true
   205  	}
   206  	for i := 0; i < len(*keystore); i++ {
   207  		ctl.symKeyIndex[*(*keystore)[i].symKeyID] = &((*keystore)[i])
   208  	}
   209  }
   210  
   211  //使对称密钥过期,使其可用于垃圾收集
   212  func (ctl *HandshakeController) releaseKey(symkeyid string, topic *Topic) bool {
   213  	if ctl.symKeyIndex[symkeyid] == nil {
   214  		log.Debug("no symkey", "symkeyid", symkeyid)
   215  		return false
   216  	}
   217  	ctl.symKeyIndex[symkeyid].expiredAt = time.Now()
   218  	log.Debug("handshake release", "symkeyid", symkeyid)
   219  	return true
   220  }
   221  
   222  //检查给定方向上的所有对称密钥
   223  //指定的对等机(公钥)和到期主题。
   224  //过期手段:
   225  //-设置了到期时间戳,超过了宽限期
   226  //-已达到消息有效性限制
   227  func (ctl *HandshakeController) cleanHandshake(pubkeyid string, topic *Topic, in bool, out bool) int {
   228  	ctl.lock.Lock()
   229  	defer ctl.lock.Unlock()
   230  	var deletecount int
   231  	var deletes []string
   232  	now := time.Now()
   233  	handshake := ctl.handshakes[pubkeyid][*topic]
   234  	log.Debug("handshake clean", "pubkey", pubkeyid, "topic", topic)
   235  	if in {
   236  		for i, key := range handshake.inKeys {
   237  			if key.expiredAt.Before(now) || (key.expiredAt.IsZero() && key.limit <= key.count) {
   238  				log.Trace("handshake in clean remove", "symkeyid", *key.symKeyID)
   239  				deletes = append(deletes, *key.symKeyID)
   240  				handshake.inKeys[deletecount] = handshake.inKeys[i]
   241  				deletecount++
   242  			}
   243  		}
   244  		handshake.inKeys = handshake.inKeys[:len(handshake.inKeys)-deletecount]
   245  	}
   246  	if out {
   247  		deletecount = 0
   248  		for i, key := range handshake.outKeys {
   249  			if key.expiredAt.Before(now) && (key.expiredAt.IsZero() && key.limit <= key.count) {
   250  				log.Trace("handshake out clean remove", "symkeyid", *key.symKeyID)
   251  				deletes = append(deletes, *key.symKeyID)
   252  				handshake.outKeys[deletecount] = handshake.outKeys[i]
   253  				deletecount++
   254  			}
   255  		}
   256  		handshake.outKeys = handshake.outKeys[:len(handshake.outKeys)-deletecount]
   257  	}
   258  	for _, keyid := range deletes {
   259  		delete(ctl.symKeyIndex, keyid)
   260  		ctl.pss.symKeyPool[keyid][*topic].protected = false
   261  	}
   262  	return len(deletes)
   263  }
   264  
   265  //对所有对等端和主题运行cleanhandshake()。
   266  func (ctl *HandshakeController) clean() {
   267  	peerpubkeys := ctl.handshakes
   268  	for pubkeyid, peertopics := range peerpubkeys {
   269  		for topic := range peertopics {
   270  			ctl.cleanHandshake(pubkeyid, &topic, true, true)
   271  		}
   272  	}
   273  }
   274  
   275  //作为主题握手的pssmsg处理程序传递将在上激活
   276  //处理传入的密钥交换消息和
   277  //按对称密钥使用的CCUNTS消息(到期限制控制)
   278  //仅当密钥处理程序失败时返回错误
   279  func (ctl *HandshakeController) handler(msg []byte, p *p2p.Peer, asymmetric bool, symkeyid string) error {
   280  	if !asymmetric {
   281  		if ctl.symKeyIndex[symkeyid] != nil {
   282  			if ctl.symKeyIndex[symkeyid].count >= ctl.symKeyIndex[symkeyid].limit {
   283  				return fmt.Errorf("discarding message using expired key: %s", symkeyid)
   284  			}
   285  			ctl.symKeyIndex[symkeyid].count++
   286  			log.Trace("increment symkey recv use", "symsymkeyid", symkeyid, "count", ctl.symKeyIndex[symkeyid].count, "limit", ctl.symKeyIndex[symkeyid].limit, "receiver", common.ToHex(crypto.FromECDSAPub(ctl.pss.PublicKey())))
   287  		}
   288  		return nil
   289  	}
   290  	keymsg := &handshakeMsg{}
   291  	err := rlp.DecodeBytes(msg, keymsg)
   292  	if err == nil {
   293  		err := ctl.handleKeys(symkeyid, keymsg)
   294  		if err != nil {
   295  			log.Error("handlekeys fail", "error", err)
   296  		}
   297  		return err
   298  	}
   299  	return nil
   300  }
   301  
   302  //处理传入密钥交换消息
   303  //将从对等端接收到的密钥添加到存储区
   304  //生成并发送对等机请求的密钥数量
   305  //
   306  //TODO:
   307  //防洪堤
   308  //-键长检查
   309  //-更新地址提示,如果:
   310  //1)新地址中最左边的字节与存储的不匹配
   311  //2)否则,如果新地址较长
   312  func (ctl *HandshakeController) handleKeys(pubkeyid string, keymsg *handshakeMsg) error {
   313  //来自对等机的新密钥
   314  	if len(keymsg.Keys) > 0 {
   315  		log.Debug("received handshake keys", "pubkeyid", pubkeyid, "from", keymsg.From, "count", len(keymsg.Keys))
   316  		var sendsymkeyids []string
   317  		for _, key := range keymsg.Keys {
   318  			sendsymkey := make([]byte, len(key))
   319  			copy(sendsymkey, key)
   320  			sendsymkeyid, err := ctl.pss.setSymmetricKey(sendsymkey, keymsg.Topic, PssAddress(keymsg.From), false, false)
   321  			if err != nil {
   322  				return err
   323  			}
   324  			sendsymkeyids = append(sendsymkeyids, sendsymkeyid)
   325  		}
   326  		if len(sendsymkeyids) > 0 {
   327  			ctl.updateKeys(pubkeyid, &keymsg.Topic, false, sendsymkeyids, keymsg.Limit)
   328  
   329  			ctl.alertHandshake(pubkeyid, sendsymkeyids)
   330  		}
   331  	}
   332  
   333  //对等密钥请求
   334  	if keymsg.Request > 0 {
   335  		_, err := ctl.sendKey(pubkeyid, &keymsg.Topic, keymsg.Request)
   336  		if err != nil {
   337  			return err
   338  		}
   339  	}
   340  
   341  	return nil
   342  }
   343  
   344  //将密钥交换发送到对“topic”有效的对等方(公钥)
   345  //将发送“keycount”指定的密钥数
   346  //“msglmit”中指定的有效限制
   347  //如果有效的传出密钥数小于理想/最大值
   348  //金额,发送一个请求以获取要补足的密钥数量
   349  //差异
   350  func (ctl *HandshakeController) sendKey(pubkeyid string, topic *Topic, keycount uint8) ([]string, error) {
   351  
   352  	var requestcount uint8
   353  	to := PssAddress{}
   354  	if _, ok := ctl.pss.pubKeyPool[pubkeyid]; !ok {
   355  		return []string{}, errors.New("Invalid public key")
   356  	} else if psp, ok := ctl.pss.pubKeyPool[pubkeyid][*topic]; ok {
   357  		to = psp.address
   358  	}
   359  
   360  	recvkeys := make([][]byte, keycount)
   361  	recvkeyids := make([]string, keycount)
   362  	ctl.lock.Lock()
   363  	if _, ok := ctl.handshakes[pubkeyid]; !ok {
   364  		ctl.handshakes[pubkeyid] = make(map[Topic]*handshake)
   365  	}
   366  	ctl.lock.Unlock()
   367  
   368  //检查缓冲区是否未满
   369  	outkeys := ctl.validKeys(pubkeyid, topic, false)
   370  	if len(outkeys) < int(ctl.symKeyCapacity) {
   371  //请求计数=uint8(self.symkeycapacity-uint8(len(outkeys)))
   372  		requestcount = ctl.symKeyCapacity
   373  	}
   374  //如果没有什么事要做就回来
   375  	if requestcount == 0 && keycount == 0 {
   376  		return []string{}, nil
   377  	}
   378  
   379  //生成要发送的新密钥
   380  	for i := 0; i < len(recvkeyids); i++ {
   381  		var err error
   382  		recvkeyids[i], err = ctl.pss.GenerateSymmetricKey(*topic, to, true)
   383  		if err != nil {
   384  			return []string{}, fmt.Errorf("set receive symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err)
   385  		}
   386  		recvkeys[i], err = ctl.pss.GetSymmetricKey(recvkeyids[i])
   387  		if err != nil {
   388  			return []string{}, fmt.Errorf("GET Generated outgoing symkey fail (pubkey %x topic %x): %v", pubkeyid, topic, err)
   389  		}
   390  	}
   391  	ctl.updateKeys(pubkeyid, topic, true, recvkeyids, ctl.symKeySendLimit)
   392  
   393  //编码并发送消息
   394  	recvkeymsg := &handshakeMsg{
   395  		From:    ctl.pss.BaseAddr(),
   396  		Keys:    recvkeys,
   397  		Request: requestcount,
   398  		Limit:   ctl.symKeySendLimit,
   399  		Topic:   *topic,
   400  	}
   401  	log.Debug("sending our symkeys", "pubkey", pubkeyid, "symkeys", recvkeyids, "limit", ctl.symKeySendLimit, "requestcount", requestcount, "keycount", len(recvkeys))
   402  	recvkeybytes, err := rlp.EncodeToBytes(recvkeymsg)
   403  	if err != nil {
   404  		return []string{}, fmt.Errorf("rlp keymsg encode fail: %v", err)
   405  	}
   406  //如果发送失败,则表示此公钥没有为此特定地址和主题注册。
   407  	err = ctl.pss.SendAsym(pubkeyid, *topic, recvkeybytes)
   408  	if err != nil {
   409  		return []string{}, fmt.Errorf("Send symkey failed: %v", err)
   410  	}
   411  	return recvkeyids, nil
   412  }
   413  
   414  //对从密钥交换请求接收的密钥启用回调
   415  func (ctl *HandshakeController) alertHandshake(pubkeyid string, symkeys []string) chan []string {
   416  	if len(symkeys) > 0 {
   417  		if _, ok := ctl.keyC[pubkeyid]; ok {
   418  			ctl.keyC[pubkeyid] <- symkeys
   419  			close(ctl.keyC[pubkeyid])
   420  			delete(ctl.keyC, pubkeyid)
   421  		}
   422  		return nil
   423  	}
   424  	if _, ok := ctl.keyC[pubkeyid]; !ok {
   425  		ctl.keyC[pubkeyid] = make(chan []string)
   426  	}
   427  	return ctl.keyC[pubkeyid]
   428  }
   429  
   430  type HandshakeAPI struct {
   431  	namespace string
   432  	ctrl      *HandshakeController
   433  }
   434  
   435  //为对等机(公钥)和主题启动握手会话
   436  //组合。
   437  //
   438  //如果设置了“sync”,则调用将被阻止,直到从对等机收到密钥为止,
   439  //或者如果握手请求超时
   440  //
   441  //如果设置了“flush”,将向对等机发送最大数量的密钥。
   442  //不管存储区中当前存在多少有效密钥。
   443  //
   444  //返回可传递给pss.getSymmetricKey()的对称密钥ID的列表
   445  //用于检索对称密钥字节本身。
   446  //
   447  //如果传入的对称密钥存储已满(并且'flush'为false),则失败,
   448  //或者如果基础密钥调度程序失败
   449  func (api *HandshakeAPI) Handshake(pubkeyid string, topic Topic, sync bool, flush bool) (keys []string, err error) {
   450  	var hsc chan []string
   451  	var keycount uint8
   452  	if flush {
   453  		keycount = api.ctrl.symKeyCapacity
   454  	} else {
   455  		validkeys := api.ctrl.validKeys(pubkeyid, &topic, false)
   456  		keycount = api.ctrl.symKeyCapacity - uint8(len(validkeys))
   457  	}
   458  	if keycount == 0 {
   459  		return keys, errors.New("Incoming symmetric key store is already full")
   460  	}
   461  	if sync {
   462  		hsc = api.ctrl.alertHandshake(pubkeyid, []string{})
   463  	}
   464  	_, err = api.ctrl.sendKey(pubkeyid, &topic, keycount)
   465  	if err != nil {
   466  		return keys, err
   467  	}
   468  	if sync {
   469  		ctx, cancel := context.WithTimeout(context.Background(), api.ctrl.symKeyRequestTimeout)
   470  		defer cancel()
   471  		select {
   472  		case keys = <-hsc:
   473  			log.Trace("sync handshake response receive", "key", keys)
   474  		case <-ctx.Done():
   475  			return []string{}, errors.New("timeout")
   476  		}
   477  	}
   478  	return keys, nil
   479  }
   480  
   481  //激活主题的握手功能
   482  func (api *HandshakeAPI) AddHandshake(topic Topic) error {
   483  	api.ctrl.deregisterFuncs[topic] = api.ctrl.pss.Register(&topic, NewHandler(api.ctrl.handler))
   484  	return nil
   485  }
   486  
   487  //停用主题的握手功能
   488  func (api *HandshakeAPI) RemoveHandshake(topic *Topic) error {
   489  	if _, ok := api.ctrl.deregisterFuncs[*topic]; ok {
   490  		api.ctrl.deregisterFuncs[*topic]()
   491  	}
   492  	return nil
   493  }
   494  
   495  //返回存储中每个对等机的所有有效对称密钥(公钥)
   496  //话题。
   497  //
   498  //“in”和“out”参数指示哪个方向
   499  //将返回对称密钥。
   500  //如果两者都为假,则不会返回任何键(也不会返回任何错误)。
   501  func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic Topic, in bool, out bool) (keys []string, err error) {
   502  	if in {
   503  		for _, inkey := range api.ctrl.validKeys(pubkeyid, &topic, true) {
   504  			keys = append(keys, *inkey)
   505  		}
   506  	}
   507  	if out {
   508  		for _, outkey := range api.ctrl.validKeys(pubkeyid, &topic, false) {
   509  			keys = append(keys, *outkey)
   510  		}
   511  	}
   512  	return keys, nil
   513  }
   514  
   515  //返回指定对称密钥的消息量
   516  //在握手方案下仍然有效
   517  func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error) {
   518  	storekey := api.ctrl.symKeyIndex[symkeyid]
   519  	if storekey == nil {
   520  		return 0, fmt.Errorf("invalid symkey id %s", symkeyid)
   521  	}
   522  	return storekey.limit - storekey.count, nil
   523  }
   524  
   525  //返回公钥的字节表示形式(以ASCII十六进制表示)
   526  //与给定的对称密钥关联
   527  func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error) {
   528  	storekey := api.ctrl.symKeyIndex[symkeyid]
   529  	if storekey == nil {
   530  		return "", fmt.Errorf("invalid symkey id %s", symkeyid)
   531  	}
   532  	return *storekey.pubKeyID, nil
   533  }
   534  
   535  //手动终止给定的symkey
   536  //
   537  //如果设置了“flush”,则在返回之前将执行垃圾收集。
   538  //
   539  //成功删除时返回true,否则返回false
   540  func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic Topic, symkeyid string, flush bool) (removed bool, err error) {
   541  	removed = api.ctrl.releaseKey(symkeyid, &topic)
   542  	if removed && flush {
   543  		api.ctrl.cleanHandshake(pubkeyid, &topic, true, true)
   544  	}
   545  	return
   546  }
   547  
   548  //在握手方案下发送对称消息
   549  //
   550  //重载pss.sendsym()API调用,添加对称密钥使用计数
   551  //用于消息到期控制
   552  func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes) (err error) {
   553  	err = api.ctrl.pss.SendSym(symkeyid, topic, msg[:])
   554  	if api.ctrl.symKeyIndex[symkeyid] != nil {
   555  		if api.ctrl.symKeyIndex[symkeyid].count >= api.ctrl.symKeyIndex[symkeyid].limit {
   556  			return errors.New("attempted send with expired key")
   557  		}
   558  		api.ctrl.symKeyIndex[symkeyid].count++
   559  		log.Trace("increment symkey send use", "symkeyid", symkeyid, "count", api.ctrl.symKeyIndex[symkeyid].count, "limit", api.ctrl.symKeyIndex[symkeyid].limit, "receiver", common.ToHex(crypto.FromECDSAPub(api.ctrl.pss.PublicKey())))
   560  	}
   561  	return err
   562  }
   563