千家信息网

怎么使用以太坊的预编译合约

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,这篇文章主要讲解了"怎么使用以太坊的预编译合约",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么使用以太坊的预编译合约"吧!以太坊包含了一些用于密码学
千家信息网最后更新 2026年11月04日怎么使用以太坊的预编译合约

这篇文章主要讲解了"怎么使用以太坊的预编译合约",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"怎么使用以太坊的预编译合约"吧!

以太坊包含了一些用于密码学计算的预编译合约,可以用来实现高级隐私保护功能。在这个教程中我们将了解以太坊提供的预编译合约清单,并通过bn256ScalarMul和bigModExp这两个实例学习以太坊预编译合约的使用方法。

1、以太坊虚拟机基本概念

在继续下面的教程之前,我们需要对以太坊和Solidity有一些基本的了解。我们关心的重点在于,以太坊有一个分布式的虚拟机即EVM,EVM提供了一组指令可以用于在区块链上执行交易并更新状态。关于EVM的一些基本概念如下:

  • storage:可以永久在链上存储信息

  • memory:EVM虚拟机的工作内存,用于保存计算过程中的变量内容

  • uint:uint256类型的别名,可保存256位,完美匹配椭圆曲线坐标的要求

  • public:用来声明函数位公开可调用

  • view:用来告诉编译器,所装饰的函数不会修改合约状态

  • pure:表示所装饰的函数不涉及合约状态的读写

2、以太坊预编译合约清单

以太坊Geth客户端的预编译合约清单看起来像这样:

var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{       common.BytesToAddress([]byte{1}): &ecrecover{},   common.BytesToAddress([]byte{2}): &sha256hash{},   common.BytesToAddress([]byte{3}): &ripemd160hash{},   common.BytesToAddress([]byte{4}): &dataCopy{},   common.BytesToAddress([]byte{5}): &bigModExp{},   common.BytesToAddress([]byte{6}): &bn256Add{},   common.BytesToAddress([]byte{7}): &bn256ScalarMul{},   common.BytesToAddress([]byte{8}): &bn256Pairing{},}

上述代码中的映射结构记录了预编译合约的地址,是最后4个是新增的预编译 合约:

bigModExp:地址0x05,执行操作:b^e mod m。bigModExp预编译合约的输入为: 底数长度、指数长度、模长度、底数即b的值、指数即e的值、模即m的值

bn256Add:地址0x06,执行操作:(x1, y1) + (x2, y2),其中x1, y1, x2, y2 都是256位的域成员,因此 (x1, y1)和 (x2, y2)都是bn256曲线上的有效点,满足公式y^2 = x^3 + 3 mod fieldOrder。 bn256预编译合约的输入就是x1, y1, x2, y2。

bn256ScalarMul:地址0x07,执行操作:k * (x, y),其中k属于群,(x,y)是曲线上的有效点。 bn256scalarMul的输入是x, y, k。

bn256Pairing:地址0x08,执行操作:配对检查e(g1, g2) = e(-h2, h3,其中g1和h2属于群G1, g2和h3属于群G2。bn256Pairing可以接收任意多对椭圆曲线上的点。群G1上的点形式为(x,y),群 G2上的点形式为(ai + b, ci + d),其中a, b, c, d (依次为虚部、实部、虚部、实部) 需要在预编译 调用时传入。bn256Pairing代码首先检查已经送出6的倍数个成员,然后执行配对检查。

x, y, a, b, c, d的值都是域成员,因此都会按域大小取模。在bn256ScalarMul中使用的k的值,则是按椭圆曲线群的阶取模。

下面我们将要学习两个主要的示例:bn256ScalarMul和bigModExp。bn256ScalarMul操作和bn256Add非常类似,而bn256Pairing操作则更像bigModExp,因为这两者都接受可变长度的输入,因此调用时需要指定输入大小。下面是调用bn256ScalarMul的代码:

function ecmul(uint ax, uint ay, uint k) public view returns(uint[2] memory p) { uint[3] memory input; input[0] = ax; input[1] = ay; input[2] = k; assembly {   if iszero(staticcall(gas, 0x07, input, 0x60, p, 0x40)) {       revert(0,0)   } } return p;}

目前内联汇编已经支持if语句,调用时设置gas数量也比以前简单 -- 在调用时使用gas,就表示利用所有可用gas,这避免了我们自己猜测需要的gas数量。

revert操作码将回滚所有的状态变化,起作用是在gas不足时或对预编译合约的调用发生故障后,可以回滚部分完成的状态更新。

3、调用bn256ScalarMul预编译合约

每个地址关联的持久化内存被称为存储(Storage),这时一个key-value库,实现从256位到256位数据的映射。在合约内这个键值库没有办法枚举,合约也不能访问其他地址关联的存储。

如果采用如下形式初始化变量:uint256 blah,那么就会将变量blah保存到持久化存储。uint是uint256的别名,如果需要更细粒度的管理,可以使用uint8,uint16等等。

EVM有一个虚拟栈可以保存256位的值。选择256位的目的是与密码学操作保持兼容。所有的EVM操作都是利用这个虚拟栈完成的,它最多可以容纳1024个成员。你可以拷贝栈顶16个成员之一,或者两两交换。所有其他的操作码都利用栈顶特定位置的成员作为输入并将结果压入栈。

对于每一个消息调用,易失内存都被复位,内存以32字节为单位分配,使用gas支付内存利用的成本。我们需要调用预编译合约的值保持在这个内存的顶部。

我们可以将之前保存在持久化存储中的变量赋值给内存,方式如下:

uint256[2] memory inputToPrecompile;input[0] = somePreviouslyStoredValue;input[1] = someOtherPreviouslyStoredValue;

这实际上就是我们在ecmul中的开始4行的操作。我们将值ax,ay,k压入虚拟栈的顶部。然后通过调用bn256ScalarMul预编译合约的地址就完成调用了。看下一部分的代码:

assembly {   if iszero(staticcall(gas, 0x07, input, 0x60, p, 0x40)) {       revert(0,0)   } }

staticcall操作码的调用形式如下:

staticcall(gasLimit, to, inputOffset, inputSize, outputOffset, outputSize)

可以看到在上面的调用bn256ScalarMul的代码中,我们:

  • 在扣除2000后,发送当前可用的gas

  • 调用地址0x07的预编译合约,这对应bn256ScalarMul

  • 使用内存变量input作为输入偏移参数

  • 将输入大小声明为0x60,这对应3个256位数值,表示一个椭圆曲线点和一个256位标量

  • 将输出保存在p中

  • 输出大小为0x40,对应要返回的椭圆曲线点

这样就完成了对以太坊预编译合约bn256ScalarMul的调用,ecmul函数的返回值现在就是bn256ScalarMul预编译合约的返回值!

4、调用bigModExp预编译合约

下面的代码调用bigModExp预编译合约:

function expmod(uint base, uint e, uint m) public view returns (uint o) {    assembly {   // define pointer   let p := mload(0x40)   // store data assembly-favouring ways   mstore(p, 0x20)             // Length of Base   mstore(add(p, 0x20), 0x20)  // Length of Exponent   mstore(add(p, 0x40), 0x20)  // Length of Modulus   mstore(add(p, 0x60), base)  // Base   mstore(add(p, 0x80), e)     // Exponent   mstore(add(p, 0xa0), m)     // Modulus   if iszero(staticcall(sub(gas, 2000), 0x05, p, 0xc0, p, 0x20)) {     revert(0, 0)   }   // data   o := mload(p)  }}

需要注意的是,0x40始终是空闲内存,因此可以使用p:=mload(0x40)来初始化内存指针。

感谢各位的阅读,以上就是"怎么使用以太坊的预编译合约"的内容了,经过本文的学习后,相信大家对怎么使用以太坊的预编译合约这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

合约 编译 以太 内存 地址 输入 曲线 代码 成员 变量 椭圆 状态 存储 学习 函数 大小 就是 形式 长度 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技有限公司 互联网是第几次科技革命的成果 北京互联网络科技有限公司 丰台科技园 互联网 易通互联网络科技有限公司 互联网 科技服务平台 新华互联网科技校企合作 互联网公司会议桌创意科技软装饰 互联网科技公司招聘 深圳前海聚择互联网科技有限公司 武汉健翔互联网络科技有限公司 智腾互联网科技有限公司 互联网科技大奖都有哪些公司 互联网科技创新的路径 伍佰万互联网科技怎么赚钱 互联网科技数字货币 本届互联网大会上的黑科技 广东鹅鹅鹅互联网科技有限公司 互联网科技接地气 美国五大互联网科技公司市值多少 互联网络科技 科技信息互联网 隆华科技工业互联网 河南猎农互联网科技有限公司 深圳无忧互联网科技 下一代互联网重大应用科技园 广州有谷有贝互联网科技有限公司 国产互联网科技公司 米泉互联网科技专业怎么样 杭州融税互联网科技有限公司
0