go libp2p kad的value可用于存放任意数据,目前kad默认在value中存放了ipnspk这两种数据(pk也是为ipns服务的,详情请阅读我的另一篇博文《ipns实现机制解读》)。

Record

Record proto:

// Record represents a dht record that contains a value
// for a key value pair
type Record struct {// The key that references this recordKey []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`// The actual value this record is storingValue []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`// Time the record was received, set by receiverTimeReceived         string   `protobuf:"bytes,5,opt,name=timeReceived,proto3" json:"timeReceived,omitempty"`
}

具体到ipns,key形式为为/ipns/{pid}

// RecordKey returns the libp2p record key for a given peer ID.
func RecordKey(pid peer.ID) string {return "/ipns/" + string(pid)
}

handlePutValue处理接收到的record,在保存之前,会先检查它,具体步骤如下:

  1. validate:检查record是否有效
  2. select:比较收到的record和本地保存的record的新旧
  3. 保存:将TimeReceived设置为当前的时间,然后保存
// Store a value in this peer local storage
func (dht *IpfsDHT) handlePutValue(ctx context.Context, p peer.ID, pmes *pb.Message) (_ *pb.Message, err error) {if len(pmes.GetKey()) == 0 {return nil, errors.New("handleGetValue but no key was provided")}rec := pmes.GetRecord()if rec == nil {logger.Debugw("got nil record from", "from", p)return nil, errors.New("nil record")}if !bytes.Equal(pmes.GetKey(), rec.GetKey()) {return nil, errors.New("put key doesn't match record key")}cleanRecord(rec)// Make sure the record is valid (not expired, valid signature etc)if err = dht.Validator.Validate(string(rec.GetKey()), rec.GetValue()); err != nil {logger.Infow("bad dht record in PUT", "from", p, "key", loggableRecordKeyBytes(rec.GetKey()), "error", err)return nil, err}dskey := convertToDsKey(rec.GetKey())// fetch the striped lock for this keyvar indexForLock byteif len(rec.GetKey()) == 0 {indexForLock = 0} else {indexForLock = rec.GetKey()[len(rec.GetKey())-1]}lk := &dht.stripedPutLocks[indexForLock]lk.Lock()defer lk.Unlock()// Make sure the new record is "better" than the record we have locally.// This prevents a record with for example a lower sequence number from// overwriting a record with a higher sequence number.existing, err := dht.getRecordFromDatastore(dskey)if err != nil {return nil, err}if existing != nil {recs := [][]byte{rec.GetValue(), existing.GetValue()}i, err := dht.Validator.Select(string(rec.GetKey()), recs)if err != nil {logger.Warnw("dht record passed validation but failed select", "from", p, "key", loggableRecordKeyBytes(rec.GetKey()), "error", err)return nil, err}if i != 0 {logger.Infow("DHT record in PUT older than existing record (ignoring)", "peer", p, "key", loggableRecordKeyBytes(rec.GetKey()))return nil, errors.New("old record")}}// record the time we receive every recordrec.TimeReceived = u.FormatRFC3339(time.Now())data, err := proto.Marshal(rec)if err != nil {return nil, err}err = dht.datastore.Put(dskey, data)return pmes, err
}

保存时,将record key的base32,作为key保存到datastore

// putLocal stores the key value pair in the datastore
func (dht *IpfsDHT) putLocal(key string, rec *recpb.Record) error {data, err := proto.Marshal(rec)if err != nil {logger.Warnw("failed to put marshal record for local put", "error", err, "key", loggableRecordKeyString(key))return err}return dht.datastore.Put(mkDsKey(key), data)
}

Validator

kad使用Validator验证value的有效性。

// Validator is an interface that should be implemented by record validators.
type Validator interface {// Validate validates the given record, returning an error if it's// invalid (e.g., expired, signed by the wrong key, etc.).Validate(key string, value []byte) error// Select selects the best record from the set of records (e.g., the// newest).//// Decisions made by select should be stable.Select(key string, values [][]byte) (int, error)
}

目前Validator主要有两种实现:

ipns::Validator
PublicKeyValidator

如前所述,pk是为ipns服务的,它的存在有些历史原因,新版的ipfs已经不需要它了,为简单起见,下面只讨论ipns
IpnsEntryipns记录的proto结构。

type IpnsEntry struct {Value        []byte                  `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`Signature    []byte                  `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"`ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=ipns.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"`Validity     []byte                  `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"`Sequence     *uint64                 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"`Ttl          *uint64                 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"`// in order for nodes to properly validate a record upon receipt, they need the public// key associated with it. For old RSA keys, its easiest if we just send this as part of// the record itself. For newer ed25519 keys, the public key can be embedded in the// peerID, making this field unnecessary.PubKey               []byte   `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"`
}

Validator 用于校验ipns记录,keyBook主要用于在peerstore里面(通过peerID)查找公钥,如果节点的公私钥是由ed25519生成的,则可以直接通过peerID反推出公钥。

// Validator is an IPNS record validator that satisfies the libp2p record
// validator interface.
type Validator struct {// KeyBook, if non-nil, will be used to lookup keys for validating IPNS// records.KeyBook pstore.KeyBook
}

Validate用于检查ipns记录的有效性,具体是先检查签名,后检查有效时间。

// Validate validates an IPNS record.
func (v Validator) Validate(key string, value []byte) error {ns, pidString, err := record.SplitKey(key)if err != nil || ns != "ipns" {return ErrInvalidPath}// Parse the value into an IpnsEntryentry := new(pb.IpnsEntry)err = proto.Unmarshal(value, entry)if err != nil {return ErrBadRecord}// Get the public key defined by the ipns pathpid, err := peer.IDFromString(pidString)if err != nil {log.Debugf("failed to parse ipns record key %s into peer ID", pidString)return ErrKeyFormat}pubk, err := v.getPublicKey(pid, entry)if err != nil {return err}return Validate(pubk, entry)
}// Validates validates the given IPNS entry against the given public key.
func Validate(pk ic.PubKey, entry *pb.IpnsEntry) error {// Check the ipns record signature with the public keyif ok, err := pk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok {return ErrSignature}eol, err := GetEOL(entry)if err != nil {return err}if time.Now().After(eol) {return ErrExpiredRecord}return nil
}

Select用于挑选最佳的ipns记录,具体步骤是:

  1. 比较sequence,sequence越大的记录越新
  2. 比较validity,validity越大的记录越新
  3. 比较value,字节码越大的记录越新
// Select selects the best record by checking which has the highest sequence
// number and latest EOL.
//
// This function returns an error if any of the records fail to parse. Validate
// your records first!
func (v Validator) Select(k string, vals [][]byte) (int, error) {var recs []*pb.IpnsEntryfor _, v := range vals {e := new(pb.IpnsEntry)if err := proto.Unmarshal(v, e); err != nil {return -1, err}recs = append(recs, e)}return selectRecord(recs, vals)
}func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {switch len(recs) {case 0:return -1, errors.New("no usable records in given set")case 1:return 0, nil}var i intfor j := 1; j < len(recs); j++ {cmp, err := Compare(recs[i], recs[j])if err != nil {return -1, err}if cmp == 0 {cmp = bytes.Compare(vals[i], vals[j])}if cmp < 0 {i = j}}return i, nil
}// Compare compares two IPNS entries. It returns:
//
// * -1 if a is older than b
// * 0 if a and b cannot be ordered (this doesn't mean that they are equal)
// * +1 if a is newer than b
func Compare(a, b *pb.IpnsEntry) (int, error) {as := a.GetSequence()bs := b.GetSequence()if as > bs {return 1, nil} else if as < bs {return -1, nil}at, err := u.ParseRFC3339(string(a.GetValidity()))if err != nil {return 0, err}bt, err := u.ParseRFC3339(string(b.GetValidity()))if err != nil {return 0, err}if at.After(bt) {return 1, nil} else if bt.After(at) {return -1, nil}return 0, nil
}

go libp2p kad record详解相关推荐

  1. 四、jOOQ 系列教程 - Record 详解

    Record 形式 Record 是jOOQ定义的用于储存数据库结果记录的一个接口,其主要是将一个表字段的列表和值的列表使用相同的顺序储存在一起,可以看做是一个用于储存列/值的映射的对象.通常有以下几 ...

  2. IP组播---IGMP、MLD、IGMP SSM Mapping、IGMP Snooping详解

    简介 Internet Group Management Protocol 互联网组管理协议,负责IPv4组播成员管理的协议 在接收者主机和组播路由器之间交互IGMP报文实现组成员的管理功能,建立.维 ...

  3. 【Oracle】record varray (associative array 关联数组) table (nested table type 嵌套表类型)和%type、%rowtype的使用详解

    官方文档: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/plsql-data-types.html#GU ...

  4. emule中节点加入Kad网络过程(源代码详解)【对原文部分改进】

    from: http://blog.csdn.net/chenbuaa/article/details/2301656 emule中节点加入Kad网络过程(源代码详解) 程序启动: EmuleDlg. ...

  5. C# 9.0新特性详解系列之五:记录(record)和with表达式

    1 背景与动机 传统面向对象编程的核心思想是一个对象有着唯一标识,表现为对象引用,封装着随时可变的属性状态,如果你改变了一个属性的状态,这个对象还是原来那个对象,就是对象引用没有因为状态的改变而改变, ...

  6. DELPHI 中 Window 消息大全使用详解

    Window 消息大全使用详解 导读: Delphi是Borland公司的一种面向对象的可视化软件开发工具. Delphi集中了Visual C++和Visual Basic两者的优点:容易上手.功能 ...

  7. getinstance方法详解_二、设计模式总览及工厂模式详解

    二.架构师内功心法之设计模式 2.架构师内功心法之设计模式 2.1.课程目标 1.通过对本章内容的学习,了解设计模式的由来. 2.介绍设计模式能帮我们解决哪些问题. 3.剖析工厂模式的历史由来及应用场 ...

  8. Rocksdb 写流程,读流程,WAL文件,MANIFEST文件,ColumnFamily,Memtable,SST文件原理详解

    文章目录 前言 Rocksdb写流程图 WAL 原理分析 概述 文件格式 查看WAL的工具 创建WAL 清理WAL MANIFEST原理分析 概述 查看MANIFEST的工具 创建 及 清除 MANI ...

  9. java中batch基础_详解Spring batch 入门学习教程(附源码)

    详解Spring batch 入门学习教程(附源码) 发布时间:2020-09-08 00:28:40 来源:脚本之家 阅读:99 作者:achuo Spring batch 是一个开源的批处理框架. ...

最新文章

  1. 什么人适合学习web前端?怎样学好web前端开发?
  2. 了解C++默默编写并调用哪些函数
  3. LSTM 为何如此有效?这五个秘密是你要知道的
  4. TypeScript 初识
  5. 单例带来的线程安全问题
  6. python面试总结(五)内存管理与MYSQL引擎选择
  7. 使用QP方法解基于五次多项式形式的cost function minimization问题
  8. android:contentInsetStart android:contentInsetEnd contentInsetStart paddingstart paddingEnd
  9. 在python中如何将两个list合并成一个list,不用for语句
  10. 软件开发 —— 极限编程(XP:Extreme Programming)
  11. Pytorch数据的读取与存储
  12. MySql 5.7.26(MySQL8)安装教程
  13. 关于USB-Audio(USB麦克风)设备的录音验证
  14. win10 电脑扬声器显示未插入
  15. 内存卡打不开需要格式化怎么恢复数据
  16. JavaWeb项目-快递代领-需求分析(二)-软件工程-小组项目
  17. 手机红米5android7.11,这些是不支持ANDROID 11更新的小米和红米手机
  18. 腾讯文档服务器异常 编辑内容暂无法保存,腾讯文档怎么编辑不了 编辑不了解决方法...
  19. unity 渐变消失_Shader Forge中怎么调整time渐变消失的间隔
  20. 评论:Monster的诺基亚Purity Pro无线耳机

热门文章

  1. java反编译教程_Java反编译工具 - JD-GUI 下载地址及使用手册
  2. 电子元器件自动化测试系统|大功率分立器件测试系统NSAT-2000
  3. java中流转pdf_Java中的PDX到PDF转换器
  4. css系统自带字体_CSS系统字体堆栈参考
  5. FFmpeg入门详解之54:搭建环境ffmpeg
  6. 谷歌地图商家抓取工具 G-Business Extractor 7.5
  7. (十二)python图形用户界面
  8. 计算机vfp的解释,整理的一些VFP基本概念
  9. inet_aton、inet_addr、inet_ntoa、inet_pton、inet_ntop
  10. 解决iTunes9在Windows2003下无法安装的问题