Ethereum 源码分析之trie

[Ethereum] 以太坊源码分析(二)分析包Trie 文章目录版本说明知识补充Merkle树Patricia TriesTrie树以太坊的MPT源码分析编码转换:encoding.go数据结构与方法node:node.gonode的结构node的方法Trie:trie.goTrie的结构Trie的方法1. Trie树的新建2. Trie树的插入3. Trie树的获取4. Trie树的删除5. Trie树的序列化和反序列化6. Trie树的cache管理Trie树的默克尔证明:proof.goTrie的加密:security_trie.gotrie包中其他的功能 阅读详情

1. Merkle Patricia Trie 源码分析

0.1 RLP编码:json 未知编码 byte 解码_以太坊RLP编码_最暖最珍贵的博客-优快云博客

RLP(RECURSIVE LENGTH PREFIX) 递归长度前缀序列化方式,还有常见的就是json序列化方式.(序列化是将任意的数据结构编码为有结构的二进制数据byte arrays)

在这里插入图片描述

RLP函数的定义(递归函数定义):

在这里插入图片描述

RLP函数的5条编码规则(通过以下规则,我们可以通过字节数组推导出值):

  1. 如果值在[0,127]之间的单个字节,其编码是其本身.

    在这里插入图片描述

  2. 如果byte数组的长度l<=55,编码的结果是数组本身,再加上128+l作为前缀.

    在这里插入图片描述

  3. 如果数组长度大于55,编码结果第一个值是183(128+55)加数组长度的编码的长度,然后是数组长度本身的编码,最后是byte数组的编码.

    比如编码一个重复1024次"a",且结果是185 1 0 97 97 97 ...

    因为长度大于55,所以1024的2进制表示:0000,0100,0000,0000

    按照大端序:0000,0000,0001,0000=> 所以按照16进制表示:0010,省略前面的0,长度为2.

  4. 如果列表(字节数组的数组) 长度小于55,编码结果第一位是192加列表长度的编码的长度,然后依次连接各个子列表的编码.在这里插入图片描述

    ["abc","def"]的编码结果是200| 131 97 98 99 | 131 100 101 102
    |是实际不存在的,只是便于我们理解而存在
    
1.1 Trie的概述

包位置:go-ethereum/trie

该包用New方法在数据库(database)构建一个trie.

  1. 无论何时trie执行一个commit操作,生成的节点都会在集合中被收集并返回.

  2. 一旦trie被提交,它就再也不会被使用(Once the trie is committed,it’s not usable anymore.)

  3. Callers 不等不在更新的trie database上重新创建一个new root的trie.

1.1.1 trie的提供的接口:
  1. New方法:建立一个trie

    func New(id *ID, db *Database) (*Trie, error)
    
  2. NewEmpty方法:构建一个空的trie(这个所谓的空是指创世区块中StateRoot所对应的MPT树)

    func NewEmpty(db *Database) *Trie {
        //types.EmptyRootHash是创世根hash的作用.
        tr, _ := New(TrieID(types.EmptyRootHash), db)
        return tr
    }
    
    // TrieID constructs an identifier for a standard trie(not a second-layer trie)
    // with provided root. It's mostly used in tests and some other tries like CHT trie.
    func TrieID(root common.Hash) *ID {
        return &ID{
             StateRoot: root,
             Owner: common.Hash{},
             Root: root,
        }
    }
    
1.2 trie package中的Trie的数据结构(重点在于reader和tracer字段)

reader字段用于检索MPT树的节点;tracer用来追踪MPT树的变化

type Trie struct{
  root node
  owner common.Hash
  //标记该Trie是否被提交,如果提交了那么该trie就不会被使用(最新状态也不可见)
  committed bool
  //追踪自从上次hash操作开始被插入的叶子节点数量
  unhashed int
  //reader是trie检索节点的处理函数
  reader *trieReader
  //tracer是追踪trie改变的工具
  // 每次提交操作之后将会被重置.
  tracer *tracer
}

其可以用如下图来表示Trie的数据结构 在这里插入图片描述

1.2.1 新建Trie
func New(id *ID, db *Database) (*Trie, error) {
    //db *Database 包是在go-ethereum/trie/database.go
    //创建可以检索MPT树的处理函数,详见1.2.2Reader
    reader, err := newTrieReader(id.StateRoot, id.Owner, db)


    if err != nil {
        return nil, err
    }
    trie := &Trie{
        owner:  id.Owner,
        reader: reader,
        tracer: newTracer(),//新建追踪器,
    }
    //接下来如何找到MPT的root,详见1.2.6 
    if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash {
        //详见1.2.7:如何寻找MPT的root node?(resolveAndTrack)
        rootnode, err := trie.resolveAndTrack(id.Root[:], nil)
        if err != nil {
            return nil, err
        }
        trie.root = rootnode
    }

}

id *ID: 数据结构表示如下: 决定了该创建的MPT树是状态树还是存储树

在这里插入图片描述

状态树(state trie)需要StateRoot和Root(且StateRoot字段和Root字段相同);存储树(storage trie)需要StateRoot,Owner(因为存储树是相对于一个智能合约的,所以需要合约地址)以及Root

在这里插入图片描述

需要值得注意的是:

ID.StateRoot是指区块的Root字段

ID.Root是指该MPT树的根节点的hash值

1.2.2 Reader(检索器)数据结构及方法

代码位置: go-ethereum/trie/trie_reader.go

// reader接口包装了trie存储的节点方法.
type Reader interface{
    Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
}
对 上述接口的实现结构体
type trieReader struct{
    owner  common.Hash
    reader Reader
    banned map[string]struct{} // Marker to prevent node from being accessed, for tests
}

创建TrieReader方法:newTrieReader()

在这里插入图片描述

实际上trieReader的reader字段是由db *Database 中的Reader方法所创建的,详见1.2.4

EmptyRootHash =common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")

上述的hash值称为创世根hash即在创世区块中的StateRoot

在这里插入图片描述

1.2.3 Database 数据结构

代码位置go-ethereum/trie/database.go

在这里插入图片描述

对于树的节点的管理,我们定义了backend接口,只要实现该接口中的方法就可以实现MPT树的节点管理.

在这里插入图片描述

1.2.4 Database.Reader()方法

在这里插入图片描述

实际上是利用hashdb package的Reader方法,详见

1.2.5 Tracer(追踪器)数据结构及方法

代码位置: go-ethereum/trie/tracer.go

该追踪器记录着增加的节点,删除的节点以及可以访问的节点列表.

type tracer struct {
    //map只是标识作用,所以value的值不重要,为了节省内存所以用空结构体.
    inserts    map[string]struct{}
    deletes    map[string]struct{}
    accessList map[string][]byte
}
1.2.6 Root(MPT树的根节点)数据结构及方法

MPT树的根节点实际上就是MPT树的节点,代码位置:

go-ethereum/trie/node.go

在MPT树中节点可以分为扩展节点(extension node),叶子节点(leaf node)以及分支节点(branch node).在实际实现中,这些节点都需要实现接口:

type node interface {
    cache() (hashNode, bool) //返回该节点与缓存相关的元数据
    //hashNode的数据结构是 []byte即32位的
    encode(w rlp.EncoderBuffer)  // 将节点编码为RLP的格式(序列化的过程)
    fstring(string) string //格式化输出字符串
}

RLP 是一种简单而有效的方式,将任意复杂的数据结构转换为字节序列

上述的encode(递归的写法)方法:

//full node的encode方法
func (n *fullNode)encode(w rlp.EncoderBuffer){
    //返回一个列表(类似一个树的先序遍历算法)
    offset := w.List()
    for _, c := range n.Children {
        if c != nil {
            c.encode(w)
        } else {
            w.Write(rlp.EmptyString)
        }
    }
    //结束列表
    w.ListEnd(offset)
}

//short node的encode方法
func (n *shortNode)encode(w *rlp.EncoderBuffer){
    offset := w.List()
    w.WriteBytes(n.Key)
    if n.Val != nil {
        n.Val.encode(w)
    } else {
        w.Write(rlp.EmptyString)
    }
    w.ListEnd(offset)
}

且扩展节点和叶子节点的数据结构类型,分支节点的数据类型分别如下:

在这里插入图片描述

fullnode中Children对应的下标为:

在这里插入图片描述

每个节点都有一个nodeFlag结构体:(go-ethereum/trie/node.go)

//nodeFlag 保存着一节点的缓存相关的元数据
type nodeFlag struct {
    //hashNode 
    hash  hashNode // cached hash of the node (may be nil)
    dirty bool     // whether the node has changes that must be written to the database
}
1.2.7 如何寻找MPT的root node? (resolveAndTrack方法)

该方法在 go-ethereum/trie/trie.go

/*
* 给定 节点的hash和路径前缀 从底层存储中加载节点
* 
*/
func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
/*
根据root的hash值返回node.
*/
    blob, err := t.reader.node(prefix, common.BytesToHash(n))
    if err != nil {
        return nil, err
    }
    t.tracer.onRead(prefix, blob)
    //将RLP编码的节点解码获得node数据结构
    return mustDecodeNode(n, blob), nil
}

以太坊中的比较重要的字节数组的长度:1. hash值(32字节) 2. state地址(20字节)

地址:/home/hypan/go-ethereum/common/types.go

在这里插入图片描述

1.3 hashdb package中的Database

该包在go-ethereum/trie/triedb/hashdb,该包中只有一个文件就是database.go

在这里插入图片描述

1.3.1 Reader()方法

go-ethereum/trie/trie_reader.go中的newTrieReader方法中调用了reader, err := db.Reader(stateRoot) 即为以下方法:

// Reader retrieves a node reader belonging to the given state root.
// An error will be returned if the requested state is not available.
func (db *Database) Reader(root common.Hash) (*reader, error) {
    if _, err := db.Node(root); err != nil {
        return nil, fmt.Errorf("state %#x is not available, %v", root, err)
    }
    return &reader{db: db}, nil
}

上述的root common.Hash 实际上是指向的是1.2.1中ID.StateRoot即区块哈希值.

db.Node方法

2. Trie中的检索器(trieReader)

go-ethereum/trie/trie_reader.go

trieReader结构体和Reader接口:

模型为:

在这里插入图片描述

为什么是这样的,推导如下:

type trieReader struct{
    //如果是状态树,那么owner common.Hash字段为空;
    //如果是存储树,那么owner则为合约地址
    owner common.Hash    
    reader Reader //Reader是接口
    banned map[string]struct{} //标记以防止节点访问,测试用
}
// Reader interface
type Reader interface{
    //给定trie的标识符(owner合约地址),节点路径和相应的节点标识符
    Node(owner common.Hash,path []byte,hash common.Hash)([]byte,error)
}
    根据创建TrieReader的newTrieReader()方法可以知道结构体中的reader Reader是由

go-ethereum/trie/database.go中的Reader方法创建// stateRoot:表示区块的root字段(状态树的根root),owner:合约地址 
func newTrieReader(stateRoot, owner common.Hash, db *Database) (*trieReader, error) {
    //types.EmptyRootHash创世根hash
    if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
        if stateRoot == (common.Hash{}) {
            log.Error("Zero state root hash!")
        }
        return &trieReader{owner: owner}, nil
    }
    reader, err := db.Reader(stateRoot) //Reader方法
    if err != nil {
        return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err}
    }
    return &trieReader{owner: owner, reader: reader}, nil
}

然后在go-ethereum/trie/database.go中调用Reader方法,创建Reader:

func (db *Database) Reader(blockRoot common.Hash) (Reader, error) {
    return db.backend.(*hashdb.Database).Reader(blockRoot)    
    // we need 断言,然后调用 go-ethereum/trie/triedb/database.go中的Reader方法
}

在go-ethereum/trie/triedb/hashdb/database.go中Reader方法:

//root common.Hash表示的是MPT树根节点hash值.
func (db *Database) Reader(root common.Hash) (*reader, error) {
    //db.Node:从内存中检索编码的缓存trie节点,如果找不到缓存的内容,
    //该方法将查询持久数据库以获取内容.
    if _, err := db.Node(root); err != nil {
        //如果找不到内容,则说明不能构建以该root为根的MPT树的读取器
        return nil, fmt.Errorf("state %#x is not available, %v", root, err)
    }
    //如果找到内容,那么就可以构建该读取器,该读取器实际上就是
    //go-ethereum/trie/triedb/hashdb/database.go中的Database
    return &reader{db: db}, nil
}

根据上述可以知道trieReader本质上就是go-ethereum/trie/triedb/hashdb/database.go总的Database数据结构.

在这里插入图片描述

2.1 trieReader结构体中node()

接口形式:

func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) {
    // Perform the logics in tests for preventing trie node access.
    if r.banned != nil {
        if _, ok := r.banned[string(path)]; ok {
            return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path}
        }
    }
    //根本不存在该trie树
    if r.reader == nil {
        return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path}
    }
    //如果是状态树,那么r.owner为nil;如果是存储树,那么r.owner为合约地址.
    //Node方法调用的是go-ethereum/trie/triedb/hashdb/database.go中的Node方法
    blob, err := r.reader.Node(r.owner, path, hash)
    if err != nil || len(blob) == 0 {
        return nil, &MissingNodeError{Owner: r.owner, NodeHash: hash, Path: path, err: err}
    }
    return blob, nil
}

功能:根据路径和trie的根节点hash找到想要rlp编码的节点

参数: path:是路径,hash是trie的根节点hash值.

返回值: rlp编码的节点.

=> go-ethereum/trie/triedb/hashdb/database.go中reader结构体的Node方法如下:

func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
    //实际上只用到了节点的hash
    blob, _ := reader.db.Node(hash)
    return blob, nil
}
=> 后期又调用该文件中Database结构体的Node方法:

// Node retrieves an encoded cached trie node from memory. If it cannot be found
// cached, the method queries the persistent database for the content.
func (db *Database) Node(hash common.Hash) ([]byte, error) {
    // It doesn't make sense to retrieve the metaroot
    if hash == (common.Hash{}) { 
        return nil, errors.New("not found")
    }
    // Retrieve the node from the clean cache if available
    if db.cleans != nil {
        if enc := db.cleans.Get(nil, hash[:]); enc != nil {
            memcacheCleanHitMeter.Mark(1)
            memcacheCleanReadMeter.Mark(int64(len(enc)))
            return enc, nil
        }
    }
    // Retrieve the node from the dirty cache if available
    db.lock.RLock()
    dirty := db.dirties[hash]
    db.lock.RUnlock()

    if dirty != nil {
        memcacheDirtyHitMeter.Mark(1)
        memcacheDirtyReadMeter.Mark(int64(len(dirty.node)))
        return dirty.node, nil
    }
    memcacheDirtyMissMeter.Mark(1)

    // Content unavailable in memory, attempt to retrieve from disk
    enc := rawdb.ReadLegacyTrieNode(db.diskdb, hash)
    if len(enc) != 0 {
        if db.cleans != nil {
            db.cleans.Set(hash[:], enc)
            memcacheCleanMissMeter.Mark(1)
            memcacheCleanWriteMeter.Mark(int64(len(enc)))
        }
        return enc, nil
    }
    return nil, errors.New("not found")
}

上述中内存中获取是否有该hash的节点(内存中GC回收的缓存clean cache+脏写的缓存dirty cache),如果上述中都没有该hash的节点,那么就从磁盘中检索该node(retrieve the node from the dirty cache if available).

3. Trie中的追踪器(tracer)

追踪器的作用:

追踪trie节点的改变,需要追踪从trie中删除和插入的节点. 被改变的节点可以分为两类: 叶子节点和中间节点. 前者插入和删除是由callers调用的,后者的插入和删除是因为需要满足trie的规则.

追踪器的数据结构:

type tracer struct{
    insers map[string]struct{}
    deletes map[string]struct{}
    accessList map[string][]byte
}
4. trie中的Get方法(key []byte)([]byte , error).

接口形式为:

func (t *Trie)Get(key []byte)([]byte,error){
    // Short circuit if the trie is already committed and not usable.
    if t.committed {
        return nil, ErrCommitted
    }
    value, newroot, didResolve, err := t.get(t.root, keybytesToHex(key), 0)
    if err == nil && didResolve {
        t.root = newroot
    }
    return value, err
}

根据上述的图片我们可以更好的理解trie.go中get方法:

//Param: 1. origNode表示的是root节点; 2. key是前缀的含义 3. pos是位置.
//Return: value:想要找点节点的value
func (t *Trie) get(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) {
    switch n := (origNode).(type) {
    case nil:
        return nil, nil, false, nil
    case valueNode: //叶子节点或者分支节点的value是value node
        return n, n, false, nil
    case *shortNode: //扩展节点
        //表示当前节点上的key部分与要寻找value的Key相对应部分的key不对应
        if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
            // key not found in trie
            return nil, n, false, nil
        }
        value, newnode, didResolve, err = t.get(n.Val, key, pos+len(n.Key))
        if err == nil && didResolve {
            n = n.copy() //不是深拷贝
            n.Val = newnode
        }
        return value, n, didResolve, err
    case *fullNode: //分支节点
        value, newnode, didResolve, err = t.get(n.Children[key[pos]], key, pos+1)
        if err == nil && didResolve {
            n = n.copy()
            n.Children[key[pos]] = newnode
        }
        return value, n, didResolve, err
    case hashNode: //拓展节点的value
        //从内存/磁盘中加载节点的过程.
        child, err := t.resolveAndTrack(n, key[:pos])
        if err != nil {
            return nil, n, true, err
        }
        value, newnode, _, err := t.get(child, key, pos)
        return value, newnode, true, err
    default:
        panic(fmt.Sprintf("%T: invalid node: %v", origNode, origNode))
    }
}
5. trie中的Update方法(key,value []byte)error

功能: 根据key值更新相应的value值. 如果value的值长度为0,那么就意味着从该trie中删除该节点.那么后续去Get该key时,就会返回nil.

接口的形式:

func (t *Trie) Update(key, value []byte) error
=>实际上调用的是func (t *Trie)update(key,value []byte)error
=>实际逻辑:
当value的长度为0时,表示删除该key-value;
当value的长度不为0时,表示插入/更新该key-value值.
func (t *Trie) update(key, value []byte) error {
    t.unhashed++
    k := keybytesToHex(key)
    if len(value) != 0 {// 插入/更新节点
        //首先要把value转化为valueNode
        _, n, err := t.insert(t.root, nil, k, valueNode(value))
        if err != nil {
            return err
        }
        t.root = n
    } else { // 删除节点
        _, n, err := t.delete(t.root, nil, k)
        if err != nil {
            return err
        }
        t.root = n
    }
    return nil
}
5.4 以太坊:以太坊源码详解4 MPT(实现快速查找以节省存储空间) 背景 Trie:用于快速检索的多叉树,查找速度快、但是需要耗费大量的存储空间 Patricia Trie:耗费的空间更小 Merkle Tree:Merkle树是一种用于快速验证内容完整性的数据结构,其基本原理是分别计算树的叶⼦结点的hash值,然后把叶⼦结点的hash值拼接在⼀起,再计算⼀次hash作为其⽗结点的hash值,依次向上直到根结点,根结点的... 阅读详情

相关推荐

大学生电赛学习之路

可以看到全国大学生电子设计大赛的认可度是非常高的,属于A类学科竞赛。电子设计大赛的形式也是给出题目,参赛者有四天三夜的时间解决问题,题目大致可以分为:控制类、测量类、高频类以及电力电子类。该竞赛会在单数年的9月份举行。但是对于新生来说电赛确实遥不可及的,可能在很多学校本科生是不能进入实验室的,但是就算进了实验室你就有资格去参加电赛或者说能拿一个不错的奖项吗? 现在正值新生开学季,当然会有一大批学生会进入实验室的学习,电赛的举办时间是每年的暑假,所以说对于大一同学来说有一年的时间去准备,那我们就来说一说参加.

weixin_46098612的博客 8788

以太坊源码解读(6)blockchain区块插入和校验分析

以太坊blockchain的管理事务: 1、blockchain模块初始化 2、blockchain模块插入校验分析 3、blockchain模块区块链分叉处理 4、blockchian模块规范链更新 上一节分析了blockchain的初始化,这一节来分析blockchain区块的插入和校验分析以及规范链更新。 一、InsertChain函数 主要功能:调用insertChain将一组区块...

lj900911的专栏 4986

Vmware 17保姆级教程:从零部署华为enspPro模拟器(附镜像下载+配置优化)

本文提供了一份详尽的Vmware 17部署华为eNSP Pro模拟器的保姆级教程。内容涵盖从镜像下载、虚拟机导入、网络配置到深度性能调优的全过程,并针对常见启动失败、设备卡顿等故障提供了实用的排查方案,旨在帮助网络工程师和华为认证备考者快速搭建稳定高效的实验环境。

arduino9maker的博客 633

WinCacheGrind 开源项目使用手册

WinCacheGrind 开源项目使用手册 1. 项目目录结构及介绍 WinCacheGrind 是一个专为Windows平台设计的cachegrind.out文件查看器,用于分析由xdebug 2生成的性能数据。以下是其基本的目录结构概述: wincachegrind/ ├── cachegrind.out File Format.md # 描述cachegrind输出文件格式的文档 ├─...

gitblog_00075的博客 529

深入解析FastDDS:SHM与DATA-SHARING传输层的性能对比与实现细节

本文深入对比了FastDDS中SHM与DATA-SHARING两种传输层的实现机制与性能差异。SHM采用端口化共享内存模型,存在两次数据拷贝;而DATA-SHARING通过共享历史实现真正的零拷贝,大幅降低延迟与CPU占用。文章结合实战测试数据,为机器人、自动驾驶等高实时性场景提供了关键的性能优化选型与配置指南。

weixin_28358083的博客 231

STC89C52RC单片机原理图详解

左侧A0和右侧B0为一 一对应的关系,左上角DIR为控制数据方向端口,如果DIR接高电平是将左侧数据A0~A8送到右侧B0~B8,如果DIR接低电平,是将右侧数据B0~B8送到左侧A0~A8,由于在电路板上LE始终与VCC连接,因此这里。74HC595是串行输入,并行输出的移位寄存器,可用3根线输入串行数据,8根线输出并行数据,多片级联后,可输出16位、24位、32位等,常用于I/O口扩展。由于数码管为共阴极,给数码管下面接口输入阳码时,LED才亮,但是直接将P0~P7的高电平输入到数码管时,

m0_50562428的博客 6万+

以太坊源码笔记-blockchain

log.Info("加载最新的本地区块", "高度", headBlock.Number(), "哈希", headBlock.Hash(), "总难度", blockTd, "时间", common.PrettyAge(time.Unix(int64(headBlock.Time()), 0))) // 输出日志,加载最新的本地区块。log.Info("链处于无状态,请等待状态同步", "number", newHeadBlock.Number, "hash", newHeadBlock.Hash())

pan_1214_的博客 968

阿里天池新闻推荐实战:多路召回策略解析与优化

本文深入解析阿里天池新闻推荐项目中的多路召回策略,包括ItemCF、UserCF、YouTubeDNN等五种召回模型的协同应用与优化技巧。通过实战案例展示如何提升推荐系统效果,特别针对冷启动问题提出创新解决方案,帮助开发者构建高效的新闻推荐系统。

weixin_26918763的博客 414

Ethereum技术架构介绍

Ethereum技术架构介绍

Make something people want. 4151

DC算法竞赛——北京PM2.5浓度回归分析

北京PM2.5浓度回归分析 比赛结果(118/1150) 一. 概述 1、项目来源:https://www.dcjingsai.com/static_page/cmpList.html 2、给定数据字段如下: 3、使用方法:回归算法 4、目的:预测北京pm2.5值,使平均预测误差越小越好。 二、分析过程 setwd("E:/R数据源") #读入数据 data <- read.csv('p...

玻璃球的博客 3474

不会路由器叠加多条宽带?看这个就够了,能让你的局域网速度更快!

通常我们在企业网络或实际项目中,随着用户的增加,一条或者几条带宽不能满足正常使用,便可以对带宽进行叠加,便于网络带度更快;通常家里有可能有两条带宽的,联通或移动的,可能都是充话费送的,但网速都不怎么样,不用可惜,用之委屈,那怎么办呢?如在迅雷上开启下载,多个资源同时下载,此时总带宽可以达到120M,也就是WAN口带宽可以叠加。多wan口路由器, 一般是有2个wan口,分别为wan1与wan2。当然也有更多的wan口的。[1] 对整个局域网来说,出口带宽数量多了,相当于总带宽上升,这样是可以起到叠加效果。

qq_43416206的博客 1111

以太坊源码分析---go-ethereum之MPT(Merkle-Patricia Trie)

本文微信公众号 月牙寂道长 文章链接为:https://mp.weixin.qq.com/s/vljKF9lI6l_fKu0_Nn0U7g 本文图片可能不太清晰,看清晰版本的,可以看原文链接微信公众号链接。   MPT(Merkle-Patricia Trie)其实就是一个数据结构,在以太坊中用于存储用户账户的状态及其变更、交易信息、交易的收据信息。   要讲MPT,就要讲讲MPT是如何...

月牙寂 1686

全球气候模式降尺度方法总结及其代码实现

简介总结各类型气候模式降尺度方法

WW、forever的博客 1万+

以太坊源码分析参考资料

Solidity v0.5.0 重大更新 这部分Solidity v0.5.0 版本一写主要的不兼容的更新,了解更新背后的原因,以及影响了那些代码,可以查看更新日志. 注解 Contracts compiled with Solidity v0.5.0 can still interface with contracts and even libraries compiled with o...

跨链技术践行者 1万+

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

go-ethereum-code-analysis 以太坊源码分析

go-ethereum代码阅读环境搭建 以太坊黄皮书 符号索引 rlp源码解析 trie源码分析 ethdb源码分析 rpc源码分析 p2p源码分析 eth协议源码分析 core源码分析 区块链索引 chain_indexer源码分析 布隆过滤器索引 bloombits源码分析 以太坊的trie树管理 回滚等操作 state源码分析 交易执行和处理部分源码分析 v...

Cypherium-japson 314

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Geth源码解析

go-ethereum-code-analysis 目录 go-ethereum代码阅读环境搭建 以太坊黄皮书 符号索引 rlp源码解析 trie源码分析 ethdb源码分析 rpc源码分析 p2p源码分析 eth协议源码分析 core源码分析 区块链索引 c...

github_3868346 1530

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

以太坊源码分析go版本

分析go-ethereum的过程,我希望从依赖比较少的底层技术组件开始,慢慢深入到核心逻辑。目录go-ethereum代码阅读环境搭建以太坊黄皮书 符号索引rlp源码解析trie源码分析ethdb源码分析rpc源码分析p2p源码分析eth协议源码分析core源码分析区块链索引 chain_indexer源码分...

Hello World程序员的博客 588

以太坊源码分析---go-ethereum之event

本文微信公众号 月牙寂道长 文章链接为:https://mp.weixin.qq.com/s/gdVIXq75MW3SrH6Of-wT2A 本文图片可能不太清晰,看清晰版本的,可以看原文链接微信公众号链接。   以太坊go-ethereum源码的模块划分非常清晰,所以其各个模块,几乎是相互独立的。   有一个以太坊的架构图: 图片来自:https://blog.youkuaiyun.com/s...

月牙寂 1978

以太坊源码分析(2)go ethereum 目录大概介绍

go-ethereum项目的组织结构基本上是按照功能模块划分的目录,下面简单介绍一下各个目录的结构     accounts            实现了一个高等级的以太坊账户管理     bmt         二进制的默克尔树的实现     build           主要是编译和构建的一些脚本和配置     cmd         命令行工具,又分了很多的命令行工具,下面一个一个介绍...

u012149181的博客 465

Ethrex深度解析:Rust打造的革命性以太坊协议实现,兼具极简设计与ZK原生特性

在区块链技术快速发展的今天,以太坊作为领先的智能合约平台,其执行客户端的性能和创新变得尤为重要。**Ethrex**作为一款用Rust语言编写的革命性以太坊协议实现,以其极简设计、模块化架构和零知识证明原生支持,为开发者和节点运营商提供了全新的选择。这个开源项目不仅追求高性能,更在ZK原生特性上进行了深度优化,为下一代区块链应用奠定了坚实基础。 ## 🚀 为什么选择Ethrex:五大核心优势

gitblog_00517的博客 880

以太坊源码阅读资料

以太坊源码分析https://blog.youkuaiyun.com/teaspring以太坊源码阅读https://github.com/dragontc/go-ethereum-code-analysis以太坊黄皮书部分翻译https://github.com/yuange1024/ethereum_yellowpaper推荐阅读顺序 从mpt源码开始读,再读account,再读block,再读block...

tianlongtc的博客 814

以太坊源码分析—账户的管理

前言 以太坊是一个巨大的状态机,在网络中,每一个全节点都保存着以太坊状态机的全部历史,只要愿意,我们可以查询到任何时刻的状态(黄皮书中 World State),其中账户状态便是其中的一部分,这部分功能由主要由state包提供 基本概念 账户地址 在以太坊中,无论是外部账户还是合约账户,都以一个160bit的数组表示地址,它是由特定椭圆...

weixin_34143774的博客 196

以太坊源码分析--MPT树

MPT(Merkle Patricia Tries)是以太坊中存储区块数据的核心数据结构,它Merkle Tree和Patricia Tree融合一个树形结构,理解MPT结构对之后学习以太坊区块header以及智能合约状态存储结构的模块源码很有帮助。 首先来看下Merkle树: 它的叶子是数据块的hash,从图中可以看出非叶子节点是其子节点串联字符串的hash,底层数据的...

区块链技术头条 1912

以太坊防篡改验证的交流

1 为什么删了以后重启没办法挖矿,是因为创世块都被删了,你重新init一下就ok了。fullnode和lightnode工作原理很多不同。。,lightnode一般就是不挖矿的 你测试当然是都得测试。 2 怎么样整个区块的数据没办法恢复 只要让广播的区块无法通过验证就行了这样就硬分叉了,比如说 你试下篡改未来的区块,比如下一个挖到的块你把root改 ,就会硬分叉。这个你有具体的日志信息?开

DDFFR的博客 1671

区块链与DApp开发(学习总结)

0 区块链认知 开发区块链与Dapp所需知识 GO 语言: 联盟链、私有链的搭建,设立接口、监听 Solidity语言: 智能合约开发,区块链上的逻辑实现 Node.js : 监听、通信、RPC JavaScript : Dapp应用程序开发 实际应用项目 电子身份、证据认证系统 供应链上下游智能合约 网上拍卖软件 众筹平台 融资、股份认证 1 Dapp开发基础知识 编写智能合约 remix 使用网页版remix编写智能合约 中文版remix网站 http://remix.hub

weixin_43494350的博客 3656
上一篇: Ethereum 源码分析之ethdb
下一篇: GTC 2025:快手《Unleash the Power of GPU Cache:Turbocharge a Short VideoRecommendation System toLightnin
Blockchain410
博客等级 码龄5年 3230粉丝 36原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blockchain410

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值