以太坊的RPC机制

2023-11-10

1 go语言的RPC机制

    RPC(Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服
务,而不需要了解底层网络细节的应用程序通信协议。RPC协议构建于TCP或UDP,或者是 HTTP
之上,允许开发者直接调用另一台计算机上的程序,而开发者无需额外地为这个调用过程编写网

络通信相关代码,使得开发包括网络分布式程序在内的应用程序更加容易。

    go语言有net/rpc包,net/rpc包允许 RPC 客户端程序通过网络或是其他 I/O 连接调用一个远端对象的公开方法
(必须是大写字母开头、可外部调用的)。在 RPC 服务端,可将一个对象注册为可访问的服务,
之后该对象的公开方法就能够以远程的方式提供访问。一个 RPC 服务端可以注册多个不同类型
的对象,但不允许注册同一类型的多个对象。

    一个对象中只有满足如下这些条件的方法,才能被 RPC 服务端设置为可供远程访问:

必须是在对象外部可公开调用的方法(首字母大写);
必须有两个参数,且参数的类型都必须是包外部可以访问的类型或者是Go内建支持的类型;
第二个参数必须是一个指针;
方法必须返回一个error类型的值。
以上4个条件,可以简单地用如下一行代码表示:
func (t *T) MethodName(argType T1, replyType *T2) error
接下来,我们来看一组 RPC 服务端和客户端交互的示例程序。

服务端:

package main;
 
import (
    "net/rpc"
    "net/http"
    "log"
)
 
//go对RPC的支持,支持三个级别:TCP、HTTP、JSONRPC
//go的RPC只支持GO开发的服务器与客户端之间的交互,因为采用了gob编码
 
//注意字段必须是导出
type Params struct {
    Width, Height int;
}
 
type Rect struct{}
 
//函数必须是导出的
//必须有两个导出类型参数
//第一个参数是接收参数
//第二个参数是返回给客户端参数,必须是指针类型
//函数还要有一个返回值error
func (r *Rect) Area(p Params, ret *int) error {
    *ret = p.Width * p.Height;
    return nil;
}
 
func (r *Rect) Perimeter(p Params, ret *int) error {
    *ret = (p.Width + p.Height) * 2;
    return nil;
}
 
func main() {
    rect := new(Rect);
    //注册一个rect服务
    rpc.Register(rect);
    //把服务处理绑定到http协议上
    rpc.HandleHTTP();
    err := http.ListenAndServe(":8080", nil);
    if err != nil {
        log.Fatal(err);
    }
}
客户端:

package main;
 
import (
    "net/rpc"
    "log"
    "fmt"
)
 
type Params struct {
    Width, Height int;
}
 
func main() {
    //连接远程rpc服务
    rpc, err := rpc.DialHTTP("tcp", "127.0.0.1:8080");
    if err != nil {
        log.Fatal(err);
    }
    ret := 0;
    //调用远程方法
    //注意第三个参数是指针类型
    err2 := rpc.Call("Rect.Area", Params{50, 100}, &ret);
    if err2 != nil {
        log.Fatal(err2);
    }
    fmt.Println(ret);
    err3 := rpc.Call("Rect.Perimeter", Params{50, 100}, &ret);
    if err3 != nil {
        log.Fatal(err3);
    }
    fmt.Println(ret);
}
2 以太坊RPC机制

以太坊启动RPC服务

以太坊客户端可以用下面方式来启动RPC监听:

geth --rpc --rpcaddr 0.0.0.0 --rpcapi db,eth,net,web3,personal --rpcport 8550
这句明命令启动了Http-RPC服务,rpc监听地址是任意ip地址,rcp使用的api接口包括db,eth,net,web,personal等,rpc端口是8550。

以太坊源码中RPC服务启动流程

在以太坊geth的main函数里,有函数

func geth(ctx *cli.Context) error {
    node := makeFullNode(ctx)
    startNode(ctx, node)
    node.Wait()
    return nil
}
这是geth的主执行函数,通过startNode()启动geth节点,startNode继续调用node/node.go中的Start()函数中,Start()函数中调用了startRPC()函数:

// startRPC is a helper method to start all the various RPC endpoint during node
// startup. It's not meant to be called at any time afterwards as it makes certain
// assumptions about the state of the node.
func (n *Node) startRPC(services map[reflect.Type]Service) error {
    // Gather all the possible APIs to surface
    apis := n.apis()
    for _, service := range services {
        apis = append(apis, service.APIs()...)
    }
    // Start the various API endpoints, terminating all in case of errors
    if err := n.startInProc(apis); err != nil {
        return err
    }
    if err := n.startIPC(apis); err != nil {
        n.stopInProc()
        return err
    }
    if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts); err != nil {
        n.stopIPC()
        n.stopInProc()
        return err
    }
    if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins, n.config.WSExposeAll); err != nil {
        n.stopHTTP()
        n.stopIPC()
        n.stopInProc()
        return err
    }
    // All API endpoints started successfully
    n.rpcAPIs = apis
    return nil
}
startRPC()收集了node中和services中所有的rpc.API类型的RPC接口,并启动了各种RPC服务形式,包括IPC、HTTP、WS、PROC等各种形式。下面分析启动Http方式的RPC函数startHTTP():

func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error {
    // Short circuit if the HTTP endpoint isn't being exposed
    if endpoint == "" {
        return nil
    }
    // Generate the whitelist based on the allowed modules
    whitelist := make(map[string]bool)
    for _, module := range modules {
        whitelist[module] = true
    }
    // Register all the APIs exposed by the services
    handler := rpc.NewServer()
    for _, api := range apis {
        if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) {
            if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
                return err
            }
            n.log.Debug("HTTP registered", "service", api.Service, "namespace", api.Namespace)
        }
    }
    // All APIs registered, start the HTTP listener
    var (
        listener net.Listener
        err      error
    )
    if listener, err = net.Listen("tcp", endpoint); err != nil {
        return err
    }
    go rpc.NewHTTPServer(cors, vhosts, handler).Serve(listener)
    n.log.Info("HTTP endpoint opened", "url", fmt.Sprintf("http://%s", endpoint), "cors", strings.Join(cors, ","), "vhosts", strings.Join(vhosts, ","))
    // All listeners booted successfully
    n.httpEndpoint = endpoint
    n.httpListener = listener
    n.httpHandler = handler
 
    return nil
}
可以看到以太坊中通过Http方式启动RPC服务的流程跟go中的rpc包启动方式基本一致。先是通过rpc.newServer()创建了Server,然后再通过registerName()注册API服务,然后启动Http监听。不过以太坊中的RPC接口API并不是按照标准RPC接口写的,它的基本形式是:

func (s *CalcService) Add(a, b int) (int, error)
符合以下标准的方法可用于远程访问:

对象必须导出
方法必须导出
方法返回0,1(响应或错误)或2(响应和错误)值
方法参数必须导出或是内置类型
方法返回值必须导出或是内置类型
客户端调用RPC服务
rpc/client.go中拨号函数:

/ The client reconnects automatically if the connection is lost.
func Dial(rawurl string) (*Client, error) {
    return DialContext(context.Background(), rawurl)
}
// DialContext creates a new RPC client, just like Dial.
//
// The context is used to cancel or time out the initial connection establishment. It does
// not affect subsequent interactions with the client.
func DialContext(ctx context.Context, rawurl string) (*Client, error) {
   u, err := url.Parse(rawurl)
   if err != nil {
      return nil, err
   }
   switch u.Scheme {
   case "http", "https":
      return DialHTTP(rawurl)
   case "ws", "wss":
      return DialWebsocket(ctx, rawurl, "")
   case "":
      return DialIPC(ctx, rawurl)
   default:
      return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
   }
}
调用RPC服务的函数:
// Call performs a JSON-RPC call with the given arguments and unmarshals into
// result if no error occurred.
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (c *Client) Call(result interface{}, method string, args ...interface{}) error {
    ctx := context.Background()
    return c.CallContext(ctx, result, method, args...)
}
// CallContext performs a JSON-RPC call with the given arguments. If the context is
// canceled before the call has successfully returned, CallContext returns immediately.
//
// The result must be a pointer so that package json can unmarshal into it. You
// can also pass nil, in which case the result is ignored.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
   msg, err := c.newMessage(method, args...)
   if err != nil {
      return err
   }
   op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
 
   if c.isHTTP {
      err = c.sendHTTP(ctx, op, msg)
   } else {
      err = c.send(ctx, op, msg)
   }
   if err != nil {
      return err
   }
 
   // dispatch has accepted the request and will close the channel it when it quits.
   switch resp, err := op.wait(ctx); {
   case err != nil:
      return err
   case resp.Error != nil:
      return resp.Error
   case len(resp.Result) == 0:
      return ErrNoResult
   default:
      return json.Unmarshal(resp.Result, &result)
   }
}
3 web3.js与控制台调用RPC接口

internal/jsre/deps下有web3.js文件,以及internal/web3ext下的web3ext.go文件,封装了可以在console控制台下访问RPC接口的方法和接口。console下面有admin.importChain方法,搜索importChain,可以看到搜索结果,

importChain对应的一个出现在web3ext.go中,

new web3._extend.Method({
    name: 'importChain',
    call: 'admin_importChain',
    params: 1
}),
函数定义在eth/api.go中:

// ImportChain imports a blockchain from a local file.
func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
    // Make sure the can access the file to import
    in, err := os.Open(file)
    if err != nil {
        return false, err
    }
    defer in.Close()
     ......
}
4 自定义RPC接口

依照ImportChain接口的方法,在eth/api.go中定义函数:

func (api *PrivateAdminAPI) TestMul(a,b *int) (int, error) {
    return (*a)*(*b),nil;
}
然后在web3ext.go中加入声明:

new web3._extend.Method({
    name: 'startRPC',
    call: 'admin_startRPC',
    params: 4,
    inputFormatter: [null, null, null, null]
}),
new web3._extend.Method({
    name: 'stopRPC',
    call: 'admin_stopRPC'
}),
new web3._extend.Method({
    name: 'startWS',
    call: 'admin_startWS',
    params: 4,
    inputFormatter: [null, null, null, null]
}),
new web3._extend.Method({
    name: 'stopWS',
    call: 'admin_stopWS'
}),
new web3._extend.Method({
    name: 'testMul',
    call: 'admin_testMul',
    params: 2
}),
],
重新编译geth,运行,在控制台输入admin:

可以看到出现了testMul接口,调用testMul接口试一下:


--------------------- 
作者:阿卡司机 
来源:CSDN 
原文:https://blog.csdn.net/liuzhijun301/article/details/80759920 
版权声明:本文为博主原创文章,转载请附上博文链接!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以太坊的RPC机制 的相关文章

  • webpack4入门笔记——打包模式选择

    什么是mode webpack4增加了mode配置项 这样会做一些默认的工作 一定程度上解决了webpacke配置复杂的弊端 语法 mode development production 复制代码 不配置默认是production 这是2种
  • Python中最小堆结构的heapq模块

    堆是非线性的树形的数据结构 完全二叉树 有两种堆 最大堆与最小堆 heapq库中的堆默认是最小堆 最大堆 树中各个父节点的值总是大于或等于任何一个子节点的值 最小堆 树中各个父节点的值总是小于或等于任何一个子节点的值 我们一般使用二叉堆来实
  • 用python送父亲节祝福,父亲节:程序员硬核示爱,“爸”气告白!

    转载来自51cto https blog 51cto com 15127557 2665130 转眼 已至6月第三周了 2020年即将过去一半 2020年06月21日 星期日 庚子年 鼠年 五月初一 也就是明天 这一天也是父亲节 父亲节 F
  • 【168】Java调用EXE并利用多线程接收EXE的输出流

    代码一共分两个类 分别是 CmdInputStreamRunnable 和 CmdUtils CmdInputStreamRunnable java import java io author 张超 操作系统执行命令时会有输出 这些输出会被

随机推荐

  • c语言中的顺序点

    http bbs csdn net topics 370153775 经常可以在一些讨论组里看到下面的提问 谁知道下面C语句给n赋什么值 m 1 n m m 最近有位不相识的朋友发email给我 问为什么在某个C 系统里 下面表达式打印出两
  • solidworks 之迈迪齿轮设计方法 粗浅解释

    之前对同步带比较了解 从来未涉及到齿轮的设计 对迈迪的工具不是了解 之前从来无法争取生成过齿轮 特此标记方法 必须要有这个标记 根据经验系数 其他的也没有尝试 之前默认选项是用户自定义 之后就可以选择模数 然后齿数 然后到 齿数决定了齿轮的
  • CTF 加解密合集

    0x00 前言 本篇的目的是对工具的收集 以及对一些题目的整理 持续更新 CTF 加解密合集 CTF Web合集 网络安全知识库 文中工具皆可关注 皓月当空w 公众号 发送关键字 工具 获取 0x01 古典加密 1 栅栏加密 CTF加密解密
  • Mac电脑——MySQL数据库root密码忘记了,不要紧,看我操作

    1 先把MySQL服务器停止运行 2 打开MySQL的文件 并修改my cnf文件 mac电脑是my cnf windows电脑是my ini 3 my cnf文件无法打开 我们可以将该文件的后缀名my cnf修改为my txt 修改完后在
  • 归零码和不归零码、单极性码和双极性码

    关于基带数字信号表示 下面有一些常见的细节 对于传输数字信号来说 最常用的方法是用不同的电压电平来表示两个二进制数字 即数字信号由矩形脉冲组成 a 单极性不归零码 无电压表示 0 恒定正电压表示 1 每个码元时间的中间点是采样时间 判决门限
  • python 获取时间间隔,Python:从时间间隔到值的映射

    I m refactoring a function that given a series of endpoints that implicitly define intervals checks if a number is inclu
  • JPA多条件查询之AND和OR混合查询

    JPA多条件查询这种业务场景是很常见的 比如说这种 像这种同一个查询条件可以多选的用OR语句来查询 比如 材质 之间选了 PU 和 橡胶 就用OR 不同查询条件之间则用AND语句查询 比如 品牌 和 材质 之间就用AND拼接 我现在要根据不
  • 相对比较全的webpack5配置

    const path require path const MiniCssExtractPlugin require mini css extract plugin const HtmlWebpackPlugin require html
  • 【论文精读】CVPR2021 - ReDet:一种用于航空目标检测的旋转等变检测器

    论文精读 CVPR2021 ReDet 一种用于航空目标检测的旋转等变检测器 论文原文 R3Det Refined Single Stage Detector with Feature Refinement for Rotating Obj
  • 成为技术传播者(一):写在前面

    这几年一直是在传说中的 高科技行业 里混迹 于是也有幸体验了很多来自高科技的悖论 譬如说 专门开发OA的一家软件公司 自己没有一套像样的OA系统 所有的文档都靠MSN传来传去 或者放在机器上开共享访问 再譬如说 给别人做敏捷方法培训的一个家
  • 使用Python 和matlab 进行AR模型的仿真(自回归模型)

    对钙信号的动力学进行建模 AR模型 import matplotlib pyplot as plt import numpy as np if name main length 500 time range length gamma 0 9
  • 用Linux搭建chrony服务器同步时间

    准备工作 在准备之前需要准备两台虚拟机 一台当做服务器使用 另一台当作虚拟机客户端使用 具体方法 鼠标右键某一个虚拟机 gt 点击管理 gt 点击克隆 提示 克隆好虚拟机后 记得修改主机名和IP地址 不然就和第一台冲突了 修改IP地址 使用
  • Tomcat源码:CoyoteAdapter、Valve#invoke、ApplicationFilterChain

    前文 Tomcat源码 启动类Bootstrap与Catalina的加载 Tomcat源码 容器的生命周期管理与事件监听 Tomcat源码 StandardServer与StandardService Tomcat源码 Container接
  • 通过百度驾车API计算一天的总行程

    前提 我们在校车上安装了GPS定位器 每5秒发送一次GPS信息到服务器后台 表结构是这个样子的 名称 行驶日志表 代码 log bus drive 注释 维护行驶日志记录 名称 代码 数据类型 限定 注释 主键 bus drive id l
  • Linux简答

    1 静态库和动态库 2 gcc编译四个阶段 预处理 编译 汇编和链接 标准I O提供了三种类型的缓冲 分别是 单缓冲 双缓冲 循环缓冲 3 Vim的三种模式 命令模式 插入模式 底行模式 i键 进入编译模式 ctrl v 进入可视模式 ES
  • 并发编程系列之volatile内存语义

    前言 前面介绍顺序一致性模型时 我们提到了程序如果正确的同步就会具备顺序一致性 这里所说的同步泛指广义上的同步 其中包括就包括同步原语volatile 那么volatile声明的变量为什么就能保证同步呢 这又是如何实现的呢 今天就让我们一起
  • js混淆与反混淆

    介绍几种js混淆方式 1 eval混淆 js中的eval 方法 就是一个js语言的执行器 它能把字符串解析成javascript的语法进行执行 简单来说就是把原本的js代码当成eval方法的参数 据说此方法出来的第一天就被破解了 修改一下代
  • 用java实现数组单元素操作

    题目 1 如果字符串中的字符为大写字母 则转换为小写字母 2 如果字符串中的字符为小写字母 则直接输出 3 如果字符串中的字符不是字母 则过滤掉 首先明确如何输入字符数组 import java util Scanner class put
  • Web项目中使用容器对象

    要是使用Maven构建项目 首先需要加入Maven依赖
  • 以太坊的RPC机制

    1 go语言的RPC机制 RPC Remote Procedure Call 远程过程调用 是一种通过网络从远程计算机程序上请求服 务 而不需要了解底层网络细节的应用程序通信协议 RPC协议构建于TCP或UDP 或者是 HTTP 之上 允许