From e7d5d6fa71a97c75943afbf97f7efb0e9ed43dc9 Mon Sep 17 00:00:00 2001 From: AurelienFT <32803821+AurelienFT@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:20:01 +0100 Subject: [PATCH] Add new ZK opcodes (#870) * Add boilerplate new opcodes * Format * Update changelog * Add more boilerplate * Add code for ec add and ec mul * Update reserved_registers * Add source code pairing * use alloc vec * Add gas costs charge * Add tests for new zk opcodes * remove forget dbg * Add more test case for zk opcodes * Remove old todo * Fix clippy in tests * Change elliptic curve point error variant * Change gas costs to v5 because v4 is already released * format * Update ec pairing to ouput directly in the register and fix the test * Update comments on opcodes * Update zk opcode to match the new spec * Update EPAR prototype * update gas costs functions * Update default gas ecop based on benches * Add tests with instructions * Change gas epar * Fomat * Update fuel-vm/src/interpreter/crypto.rs Co-authored-by: Hannes Karppila <2204863+Dentosal@users.noreply.github.com> * cleanup for add zk opcodes PR (#874) * Simplify error types * Use MemoryOverflow instead of ArithmeticOverflow * Fix inversion letter --------- Co-authored-by: AurelienFT * Add some doc about the usage of operation symbols + and * * Add memory overflow tests * fix clippy --------- Co-authored-by: Hannes Karppila <2204863+Dentosal@users.noreply.github.com> --- CHANGELOG.md | 3 + fuel-asm/src/lib.rs | 9 +- fuel-asm/src/panic_reason.rs | 7 + .../transaction/consensus_parameters/gas.rs | 526 ++++++++++++++- .../gas/default_gas_costs.rs | 7 +- fuel-vm/Cargo.toml | 2 + fuel-vm/src/interpreter/crypto.rs | 197 ++++++ fuel-vm/src/interpreter/crypto/tests.rs | 625 ++++++++++++++++++ .../src/interpreter/executors/instruction.rs | 16 + .../instruction/tests/reserved_registers.rs | 11 + fuel-vm/src/tests/crypto.rs | 222 +++++++ fuel-vm/src/tests/predicate.rs | 6 +- 12 files changed, 1622 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6dfcfcdb..a7e855e89b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- [870](https://github.com/FuelLabs/fuel-vm/pull/870): Add 3 new ZK-related opcodes: eadd (ecAdd on EVM), emul (ecMul on EVM), epar (ecPairing on EVM) + ### Fixed - [860](https://github.com/FuelLabs/fuel-vm/pull/860): Fixed missing fuzzing coverage report in CI. diff --git a/fuel-asm/src/lib.rs b/fuel-asm/src/lib.rs index 84cf37df29..20640ba257 100644 --- a/fuel-asm/src/lib.rs +++ b/fuel-asm/src/lib.rs @@ -345,6 +345,10 @@ impl_instructions! { 0xba BSIZ bsiz [dst: RegId blob_id_ptr: RegId] "Load blob as data" 0xbb BLDD bldd [dst_ptr: RegId blob_id_ptr: RegId offset: RegId len: RegId] + "Given some curve, performs an operation on points" + 0xbc ECOP ecop [dst: RegId curve_id: RegId operation_type: RegId points_ptr: RegId] + "Given some curve, performs a pairing on groups of points" + 0xbe EPAR epar [success: RegId curve_id: RegId number_elements: RegId points_ptr: RegId] } impl Instruction { @@ -701,9 +705,8 @@ impl Opcode { | K256 | S256 | NOOP | FLAG | ADDI | ANDI | DIVI | EXPI | MODI | MULI | MLDV | ORI | SLLI | SRLI | SUBI | XORI | JNEI | LB | LW | SB | SW | MCPI | MCLI | GM | MOVI | JNZI | JI | JMP | JNE | JMPF | JMPB | JNZF - | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS | GTF | LDC | BSIZ | BLDD => { - true - } + | JNZB | JNEF | JNEB | CFEI | CFSI | CFE | CFS | GTF | LDC | BSIZ | BLDD + | ECOP | EPAR => true, _ => false, } } diff --git a/fuel-asm/src/panic_reason.rs b/fuel-asm/src/panic_reason.rs index 3dc9881c7c..15882fbe07 100644 --- a/fuel-asm/src/panic_reason.rs +++ b/fuel-asm/src/panic_reason.rs @@ -152,6 +152,13 @@ enum_from! { BlobIdAlreadyUploaded = 0x37, /// Active gas costs do not define the cost for this instruction. GasCostNotDefined = 0x38, + /// The curve id is not supported. + UnsupportedCurveId = 0x39, + /// The operation type is not supported. + UnsupportedOperationType = 0x3a, + /// Read alt_bn_128 curve point is invalid. + InvalidEllipticCurvePoint = 0x3b, + } } diff --git a/fuel-tx/src/transaction/consensus_parameters/gas.rs b/fuel-tx/src/transaction/consensus_parameters/gas.rs index c455164b4d..2735d682df 100644 --- a/fuel-tx/src/transaction/consensus_parameters/gas.rs +++ b/fuel-tx/src/transaction/consensus_parameters/gas.rs @@ -80,6 +80,8 @@ pub enum GasCostsValues { V3(GasCostsValuesV3), /// Version 4 of the gas costs. V4(GasCostsValuesV4), + /// Version 5 of the gas costs. + V5(GasCostsValuesV5), } /// Gas cost for this instruction is not defined for this version. @@ -99,6 +101,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.add, GasCostsValues::V3(v3) => v3.add, GasCostsValues::V4(v4) => v4.add, + GasCostsValues::V5(v5) => v5.add, } } @@ -108,6 +111,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.addi, GasCostsValues::V3(v3) => v3.addi, GasCostsValues::V4(v4) => v4.addi, + GasCostsValues::V5(v5) => v5.addi, } } @@ -117,6 +121,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.and, GasCostsValues::V3(v3) => v3.and, GasCostsValues::V4(v4) => v4.and, + GasCostsValues::V5(v5) => v5.and, } } @@ -126,6 +131,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.andi, GasCostsValues::V3(v3) => v3.andi, GasCostsValues::V4(v4) => v4.andi, + GasCostsValues::V5(v5) => v5.andi, } } @@ -135,6 +141,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.bal, GasCostsValues::V3(v3) => v3.bal, GasCostsValues::V4(v4) => v4.bal, + GasCostsValues::V5(v5) => v5.bal, } } @@ -144,6 +151,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.bhei, GasCostsValues::V3(v3) => v3.bhei, GasCostsValues::V4(v4) => v4.bhei, + GasCostsValues::V5(v5) => v5.bhei, } } @@ -153,6 +161,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.bhsh, GasCostsValues::V3(v3) => v3.bhsh, GasCostsValues::V4(v4) => v4.bhsh, + GasCostsValues::V5(v5) => v5.bhsh, } } @@ -162,6 +171,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.burn, GasCostsValues::V3(v3) => v3.burn, GasCostsValues::V4(v4) => v4.burn, + GasCostsValues::V5(v5) => v5.burn, } } @@ -171,6 +181,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.cb, GasCostsValues::V3(v3) => v3.cb, GasCostsValues::V4(v4) => v4.cb, + GasCostsValues::V5(v5) => v5.cb, } } @@ -180,6 +191,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.cfsi, GasCostsValues::V3(v3) => v3.cfsi, GasCostsValues::V4(v4) => v4.cfsi, + GasCostsValues::V5(v5) => v5.cfsi, } } @@ -189,6 +201,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.div, GasCostsValues::V3(v3) => v3.div, GasCostsValues::V4(v4) => v4.div, + GasCostsValues::V5(v5) => v5.div, } } @@ -198,6 +211,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.divi, GasCostsValues::V3(v3) => v3.divi, GasCostsValues::V4(v4) => v4.divi, + GasCostsValues::V5(v5) => v5.divi, } } @@ -207,6 +221,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.eck1, GasCostsValues::V3(v3) => v3.eck1, GasCostsValues::V4(v4) => v4.eck1, + GasCostsValues::V5(v5) => v5.eck1, } } @@ -216,6 +231,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.ecr1, GasCostsValues::V3(v3) => v3.ecr1, GasCostsValues::V4(v4) => v4.ecr1, + GasCostsValues::V5(v5) => v5.ecr1, } } @@ -225,6 +241,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.eq, GasCostsValues::V3(v3) => v3.eq, GasCostsValues::V4(v4) => v4.eq, + GasCostsValues::V5(v5) => v5.eq, } } @@ -234,6 +251,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.exp, GasCostsValues::V3(v3) => v3.exp, GasCostsValues::V4(v4) => v4.exp, + GasCostsValues::V5(v5) => v5.exp, } } @@ -243,6 +261,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.expi, GasCostsValues::V3(v3) => v3.expi, GasCostsValues::V4(v4) => v4.expi, + GasCostsValues::V5(v5) => v5.expi, } } @@ -252,6 +271,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.flag, GasCostsValues::V3(v3) => v3.flag, GasCostsValues::V4(v4) => v4.flag, + GasCostsValues::V5(v5) => v5.flag, } } @@ -261,6 +281,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.gm, GasCostsValues::V3(v3) => v3.gm, GasCostsValues::V4(v4) => v4.gm, + GasCostsValues::V5(v5) => v5.gm, } } @@ -270,6 +291,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.gt, GasCostsValues::V3(v3) => v3.gt, GasCostsValues::V4(v4) => v4.gt, + GasCostsValues::V5(v5) => v5.gt, } } @@ -279,6 +301,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.gtf, GasCostsValues::V3(v3) => v3.gtf, GasCostsValues::V4(v4) => v4.gtf, + GasCostsValues::V5(v5) => v5.gtf, } } @@ -288,6 +311,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.ji, GasCostsValues::V3(v3) => v3.ji, GasCostsValues::V4(v4) => v4.ji, + GasCostsValues::V5(v5) => v5.ji, } } @@ -297,6 +321,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jmp, GasCostsValues::V3(v3) => v3.jmp, GasCostsValues::V4(v4) => v4.jmp, + GasCostsValues::V5(v5) => v5.jmp, } } @@ -306,6 +331,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jne, GasCostsValues::V3(v3) => v3.jne, GasCostsValues::V4(v4) => v4.jne, + GasCostsValues::V5(v5) => v5.jne, } } @@ -315,6 +341,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jnei, GasCostsValues::V3(v3) => v3.jnei, GasCostsValues::V4(v4) => v4.jnei, + GasCostsValues::V5(v5) => v5.jnei, } } @@ -324,6 +351,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jnzi, GasCostsValues::V3(v3) => v3.jnzi, GasCostsValues::V4(v4) => v4.jnzi, + GasCostsValues::V5(v5) => v5.jnzi, } } @@ -333,6 +361,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jmpf, GasCostsValues::V3(v3) => v3.jmpf, GasCostsValues::V4(v4) => v4.jmpf, + GasCostsValues::V5(v5) => v5.jmpf, } } @@ -342,6 +371,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jmpb, GasCostsValues::V3(v3) => v3.jmpb, GasCostsValues::V4(v4) => v4.jmpb, + GasCostsValues::V5(v5) => v5.jmpb, } } @@ -351,6 +381,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jnzf, GasCostsValues::V3(v3) => v3.jnzf, GasCostsValues::V4(v4) => v4.jnzf, + GasCostsValues::V5(v5) => v5.jnzf, } } @@ -360,6 +391,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jnzb, GasCostsValues::V3(v3) => v3.jnzb, GasCostsValues::V4(v4) => v4.jnzb, + GasCostsValues::V5(v5) => v5.jnzb, } } @@ -369,6 +401,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jnef, GasCostsValues::V3(v3) => v3.jnef, GasCostsValues::V4(v4) => v4.jnef, + GasCostsValues::V5(v5) => v5.jnef, } } @@ -378,6 +411,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.jneb, GasCostsValues::V3(v3) => v3.jneb, GasCostsValues::V4(v4) => v4.jneb, + GasCostsValues::V5(v5) => v5.jneb, } } @@ -387,6 +421,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.lb, GasCostsValues::V3(v3) => v3.lb, GasCostsValues::V4(v4) => v4.lb, + GasCostsValues::V5(v5) => v5.lb, } } @@ -396,6 +431,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.log, GasCostsValues::V3(v3) => v3.log, GasCostsValues::V4(v4) => v4.log, + GasCostsValues::V5(v5) => v5.log, } } @@ -405,6 +441,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.lt, GasCostsValues::V3(v3) => v3.lt, GasCostsValues::V4(v4) => v4.lt, + GasCostsValues::V5(v5) => v5.lt, } } @@ -414,6 +451,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.lw, GasCostsValues::V3(v3) => v3.lw, GasCostsValues::V4(v4) => v4.lw, + GasCostsValues::V5(v5) => v5.lw, } } @@ -423,6 +461,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mint, GasCostsValues::V3(v3) => v3.mint, GasCostsValues::V4(v4) => v4.mint, + GasCostsValues::V5(v5) => v5.mint, } } @@ -432,6 +471,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mlog, GasCostsValues::V3(v3) => v3.mlog, GasCostsValues::V4(v4) => v4.mlog, + GasCostsValues::V5(v5) => v5.mlog, } } @@ -441,6 +481,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mod_op, GasCostsValues::V3(v3) => v3.mod_op, GasCostsValues::V4(v4) => v4.mod_op, + GasCostsValues::V5(v5) => v5.mod_op, } } @@ -450,6 +491,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.modi, GasCostsValues::V3(v3) => v3.modi, GasCostsValues::V4(v4) => v4.modi, + GasCostsValues::V5(v5) => v5.modi, } } @@ -459,6 +501,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.move_op, GasCostsValues::V3(v3) => v3.move_op, GasCostsValues::V4(v4) => v4.move_op, + GasCostsValues::V5(v5) => v5.move_op, } } @@ -468,6 +511,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.movi, GasCostsValues::V3(v3) => v3.movi, GasCostsValues::V4(v4) => v4.movi, + GasCostsValues::V5(v5) => v5.movi, } } @@ -477,6 +521,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mroo, GasCostsValues::V3(v3) => v3.mroo, GasCostsValues::V4(v4) => v4.mroo, + GasCostsValues::V5(v5) => v5.mroo, } } @@ -486,6 +531,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mul, GasCostsValues::V3(v3) => v3.mul, GasCostsValues::V4(v4) => v4.mul, + GasCostsValues::V5(v5) => v5.mul, } } @@ -495,6 +541,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.muli, GasCostsValues::V3(v3) => v3.muli, GasCostsValues::V4(v4) => v4.muli, + GasCostsValues::V5(v5) => v5.muli, } } @@ -504,6 +551,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mldv, GasCostsValues::V3(v3) => v3.mldv, GasCostsValues::V4(v4) => v4.mldv, + GasCostsValues::V5(v5) => v5.mldv, } } @@ -513,6 +561,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.noop, GasCostsValues::V3(v3) => v3.noop, GasCostsValues::V4(v4) => v4.noop, + GasCostsValues::V5(v5) => v5.noop, } } @@ -522,6 +571,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.not, GasCostsValues::V3(v3) => v3.not, GasCostsValues::V4(v4) => v4.not, + GasCostsValues::V5(v5) => v5.not, } } @@ -531,6 +581,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.or, GasCostsValues::V3(v3) => v3.or, GasCostsValues::V4(v4) => v4.or, + GasCostsValues::V5(v5) => v5.or, } } @@ -540,6 +591,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.ori, GasCostsValues::V3(v3) => v3.ori, GasCostsValues::V4(v4) => v4.ori, + GasCostsValues::V5(v5) => v5.ori, } } @@ -549,6 +601,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.poph, GasCostsValues::V3(v3) => v3.poph, GasCostsValues::V4(v4) => v4.poph, + GasCostsValues::V5(v5) => v5.poph, } } @@ -558,6 +611,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.popl, GasCostsValues::V3(v3) => v3.popl, GasCostsValues::V4(v4) => v4.popl, + GasCostsValues::V5(v5) => v5.popl, } } @@ -567,6 +621,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.pshh, GasCostsValues::V3(v3) => v3.pshh, GasCostsValues::V4(v4) => v4.pshh, + GasCostsValues::V5(v5) => v5.pshh, } } @@ -576,6 +631,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.pshl, GasCostsValues::V3(v3) => v3.pshl, GasCostsValues::V4(v4) => v4.pshl, + GasCostsValues::V5(v5) => v5.pshl, } } @@ -585,6 +641,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.ret, GasCostsValues::V3(v3) => v3.ret, GasCostsValues::V4(v4) => v4.ret, + GasCostsValues::V5(v5) => v5.ret, } } @@ -594,6 +651,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.rvrt, GasCostsValues::V3(v3) => v3.rvrt, GasCostsValues::V4(v4) => v4.rvrt, + GasCostsValues::V5(v5) => v5.rvrt, } } @@ -603,6 +661,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.sb, GasCostsValues::V3(v3) => v3.sb, GasCostsValues::V4(v4) => v4.sb, + GasCostsValues::V5(v5) => v5.sb, } } @@ -612,6 +671,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.sll, GasCostsValues::V3(v3) => v3.sll, GasCostsValues::V4(v4) => v4.sll, + GasCostsValues::V5(v5) => v5.sll, } } @@ -621,6 +681,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.slli, GasCostsValues::V3(v3) => v3.slli, GasCostsValues::V4(v4) => v4.slli, + GasCostsValues::V5(v5) => v5.slli, } } @@ -630,6 +691,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.srl, GasCostsValues::V3(v3) => v3.srl, GasCostsValues::V4(v4) => v4.srl, + GasCostsValues::V5(v5) => v5.srl, } } @@ -639,6 +701,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.srli, GasCostsValues::V3(v3) => v3.srli, GasCostsValues::V4(v4) => v4.srli, + GasCostsValues::V5(v5) => v5.srli, } } @@ -648,6 +711,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.srw, GasCostsValues::V3(v3) => v3.srw, GasCostsValues::V4(v4) => v4.srw, + GasCostsValues::V5(v5) => v5.srw, } } @@ -657,6 +721,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.sub, GasCostsValues::V3(v3) => v3.sub, GasCostsValues::V4(v4) => v4.sub, + GasCostsValues::V5(v5) => v5.sub, } } @@ -666,6 +731,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.subi, GasCostsValues::V3(v3) => v3.subi, GasCostsValues::V4(v4) => v4.subi, + GasCostsValues::V5(v5) => v5.subi, } } @@ -675,6 +741,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.sw, GasCostsValues::V3(v3) => v3.sw, GasCostsValues::V4(v4) => v4.sw, + GasCostsValues::V5(v5) => v5.sw, } } @@ -684,6 +751,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.sww, GasCostsValues::V3(v3) => v3.sww, GasCostsValues::V4(v4) => v4.sww, + GasCostsValues::V5(v5) => v5.sww, } } @@ -693,6 +761,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.time, GasCostsValues::V3(v3) => v3.time, GasCostsValues::V4(v4) => v4.time, + GasCostsValues::V5(v5) => v5.time, } } @@ -702,6 +771,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.tr, GasCostsValues::V3(v3) => v3.tr, GasCostsValues::V4(v4) => v4.tr, + GasCostsValues::V5(v5) => v5.tr, } } @@ -711,6 +781,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.tro, GasCostsValues::V3(v3) => v3.tro, GasCostsValues::V4(v4) => v4.tro, + GasCostsValues::V5(v5) => v5.tro, } } @@ -720,6 +791,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wdcm, GasCostsValues::V3(v3) => v3.wdcm, GasCostsValues::V4(v4) => v4.wdcm, + GasCostsValues::V5(v5) => v5.wdcm, } } @@ -729,6 +801,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqcm, GasCostsValues::V3(v3) => v3.wqcm, GasCostsValues::V4(v4) => v4.wqcm, + GasCostsValues::V5(v5) => v5.wqcm, } } @@ -738,6 +811,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wdop, GasCostsValues::V3(v3) => v3.wdop, GasCostsValues::V4(v4) => v4.wdop, + GasCostsValues::V5(v5) => v5.wdop, } } @@ -747,6 +821,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqop, GasCostsValues::V3(v3) => v3.wqop, GasCostsValues::V4(v4) => v4.wqop, + GasCostsValues::V5(v5) => v5.wqop, } } @@ -756,6 +831,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wdml, GasCostsValues::V3(v3) => v3.wdml, GasCostsValues::V4(v4) => v4.wdml, + GasCostsValues::V5(v5) => v5.wdml, } } @@ -765,6 +841,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqml, GasCostsValues::V3(v3) => v3.wqml, GasCostsValues::V4(v4) => v4.wqml, + GasCostsValues::V5(v5) => v5.wqml, } } @@ -774,6 +851,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wddv, GasCostsValues::V3(v3) => v3.wddv, GasCostsValues::V4(v4) => v4.wddv, + GasCostsValues::V5(v5) => v5.wddv, } } @@ -783,6 +861,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqdv, GasCostsValues::V3(v3) => v3.wqdv, GasCostsValues::V4(v4) => v4.wqdv, + GasCostsValues::V5(v5) => v5.wqdv, } } @@ -792,6 +871,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wdmd, GasCostsValues::V3(v3) => v3.wdmd, GasCostsValues::V4(v4) => v4.wdmd, + GasCostsValues::V5(v5) => v5.wdmd, } } @@ -801,6 +881,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqmd, GasCostsValues::V3(v3) => v3.wqmd, GasCostsValues::V4(v4) => v4.wqmd, + GasCostsValues::V5(v5) => v5.wqmd, } } @@ -810,6 +891,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wdam, GasCostsValues::V3(v3) => v3.wdam, GasCostsValues::V4(v4) => v4.wdam, + GasCostsValues::V5(v5) => v5.wdam, } } @@ -819,6 +901,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqam, GasCostsValues::V3(v3) => v3.wqam, GasCostsValues::V4(v4) => v4.wqam, + GasCostsValues::V5(v5) => v5.wqam, } } @@ -828,6 +911,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wdmm, GasCostsValues::V3(v3) => v3.wdmm, GasCostsValues::V4(v4) => v4.wdmm, + GasCostsValues::V5(v5) => v5.wdmm, } } @@ -837,6 +921,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.wqmm, GasCostsValues::V3(v3) => v3.wqmm, GasCostsValues::V4(v4) => v4.wqmm, + GasCostsValues::V5(v5) => v5.wqmm, } } @@ -846,6 +931,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.xor, GasCostsValues::V3(v3) => v3.xor, GasCostsValues::V4(v4) => v4.xor, + GasCostsValues::V5(v5) => v5.xor, } } @@ -855,6 +941,17 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.xori, GasCostsValues::V3(v3) => v3.xori, GasCostsValues::V4(v4) => v4.xori, + GasCostsValues::V5(v5) => v5.xori, + } + } + + pub fn ecop(&self) -> Result { + match self { + GasCostsValues::V1(_) => Err(GasCostNotDefined), + GasCostsValues::V2(_) => Err(GasCostNotDefined), + GasCostsValues::V3(_) => Err(GasCostNotDefined), + GasCostsValues::V4(_) => Err(GasCostNotDefined), + GasCostsValues::V5(v5) => Ok(v5.ecop), } } @@ -867,6 +964,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.aloc, GasCostsValues::V3(v3) => v3.aloc, GasCostsValues::V4(v4) => v4.aloc, + GasCostsValues::V5(v5) => v5.aloc, } } @@ -882,6 +980,7 @@ impl GasCostsValues { }, GasCostsValues::V3(v3) => v3.cfe, GasCostsValues::V4(v4) => v4.cfe, + GasCostsValues::V5(v5) => v5.cfe, } } @@ -897,6 +996,7 @@ impl GasCostsValues { }, GasCostsValues::V3(v3) => v3.cfei, GasCostsValues::V4(v4) => v4.cfei, + GasCostsValues::V5(v5) => v5.cfei, } } @@ -906,6 +1006,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.call, GasCostsValues::V3(v3) => v3.call, GasCostsValues::V4(v4) => v4.call, + GasCostsValues::V5(v5) => v5.call, } } @@ -915,6 +1016,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.ccp, GasCostsValues::V3(v3) => v3.ccp, GasCostsValues::V4(v4) => v4.ccp, + GasCostsValues::V5(v5) => v5.ccp, } } @@ -924,6 +1026,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.croo, GasCostsValues::V3(v3) => v3.croo, GasCostsValues::V4(v4) => v4.croo, + GasCostsValues::V5(v5) => v5.croo, } } @@ -933,6 +1036,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.csiz, GasCostsValues::V3(v3) => v3.csiz, GasCostsValues::V4(v4) => v4.csiz, + GasCostsValues::V5(v5) => v5.csiz, } } @@ -951,6 +1055,7 @@ impl GasCostsValues { gas_per_unit: 0, }, GasCostsValues::V4(v4) => v4.ed19, + GasCostsValues::V5(v5) => v5.ed19, } } @@ -960,6 +1065,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.k256, GasCostsValues::V3(v3) => v3.k256, GasCostsValues::V4(v4) => v4.k256, + GasCostsValues::V5(v5) => v5.k256, } } @@ -969,6 +1075,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.ldc, GasCostsValues::V3(v3) => v3.ldc, GasCostsValues::V4(v4) => v4.ldc, + GasCostsValues::V5(v5) => v5.ldc, } } @@ -978,6 +1085,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.logd, GasCostsValues::V3(v3) => v3.logd, GasCostsValues::V4(v4) => v4.logd, + GasCostsValues::V5(v5) => v5.logd, } } @@ -987,6 +1095,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mcl, GasCostsValues::V3(v3) => v3.mcl, GasCostsValues::V4(v4) => v4.mcl, + GasCostsValues::V5(v5) => v5.mcl, } } @@ -996,6 +1105,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mcli, GasCostsValues::V3(v3) => v3.mcli, GasCostsValues::V4(v4) => v4.mcli, + GasCostsValues::V5(v5) => v5.mcli, } } @@ -1005,6 +1115,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mcp, GasCostsValues::V3(v3) => v3.mcp, GasCostsValues::V4(v4) => v4.mcp, + GasCostsValues::V5(v5) => v5.mcp, } } @@ -1014,6 +1125,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.mcpi, GasCostsValues::V3(v3) => v3.mcpi, GasCostsValues::V4(v4) => v4.mcpi, + GasCostsValues::V5(v5) => v5.mcpi, } } @@ -1023,6 +1135,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.meq, GasCostsValues::V3(v3) => v3.meq, GasCostsValues::V4(v4) => v4.meq, + GasCostsValues::V5(v5) => v5.meq, } } @@ -1032,6 +1145,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.retd, GasCostsValues::V3(v3) => v3.retd, GasCostsValues::V4(v4) => v4.retd, + GasCostsValues::V5(v5) => v5.retd, } } @@ -1041,6 +1155,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.s256, GasCostsValues::V3(v3) => v3.s256, GasCostsValues::V4(v4) => v4.s256, + GasCostsValues::V5(v5) => v5.s256, } } @@ -1050,6 +1165,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.scwq, GasCostsValues::V3(v3) => v3.scwq, GasCostsValues::V4(v4) => v4.scwq, + GasCostsValues::V5(v5) => v5.scwq, } } @@ -1059,6 +1175,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.smo, GasCostsValues::V3(v3) => v3.smo, GasCostsValues::V4(v4) => v4.smo, + GasCostsValues::V5(v5) => v5.smo, } } @@ -1068,6 +1185,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.srwq, GasCostsValues::V3(v3) => v3.srwq, GasCostsValues::V4(v4) => v4.srwq, + GasCostsValues::V5(v5) => v5.srwq, } } @@ -1077,6 +1195,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.swwq, GasCostsValues::V3(v3) => v3.swwq, GasCostsValues::V4(v4) => v4.swwq, + GasCostsValues::V5(v5) => v5.swwq, } } @@ -1086,6 +1205,7 @@ impl GasCostsValues { GasCostsValues::V2(_v2) => Err(GasCostNotDefined), GasCostsValues::V3(_v3) => Err(GasCostNotDefined), GasCostsValues::V4(v4) => Ok(v4.bsiz), + GasCostsValues::V5(v5) => Ok(v5.bsiz), } } @@ -1095,6 +1215,17 @@ impl GasCostsValues { GasCostsValues::V2(_v2) => Err(GasCostNotDefined), GasCostsValues::V3(_v3) => Err(GasCostNotDefined), GasCostsValues::V4(v4) => Ok(v4.bldd), + GasCostsValues::V5(v5) => Ok(v5.bldd), + } + } + + pub fn epar(&self) -> Result { + match self { + GasCostsValues::V1(_v1) => Err(GasCostNotDefined), + GasCostsValues::V2(_v2) => Err(GasCostNotDefined), + GasCostsValues::V3(_v3) => Err(GasCostNotDefined), + GasCostsValues::V4(_v4) => Err(GasCostNotDefined), + GasCostsValues::V5(v5) => Ok(v5.epar), } } @@ -1104,6 +1235,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.contract_root, GasCostsValues::V3(v3) => v3.contract_root, GasCostsValues::V4(v4) => v4.contract_root, + GasCostsValues::V5(v5) => v5.contract_root, } } @@ -1113,6 +1245,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.state_root, GasCostsValues::V3(v3) => v3.state_root, GasCostsValues::V4(v4) => v4.state_root, + GasCostsValues::V5(v5) => v5.state_root, } } @@ -1122,6 +1255,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.new_storage_per_byte, GasCostsValues::V3(v3) => v3.new_storage_per_byte, GasCostsValues::V4(v4) => v4.new_storage_per_byte, + GasCostsValues::V5(v5) => v5.new_storage_per_byte, } } @@ -1131,6 +1265,7 @@ impl GasCostsValues { GasCostsValues::V2(v2) => v2.vm_initialization, GasCostsValues::V3(v3) => v3.vm_initialization, GasCostsValues::V4(v4) => v4.vm_initialization, + GasCostsValues::V5(v5) => v5.vm_initialization, } } } @@ -1651,6 +1786,139 @@ pub struct GasCostsValuesV4 { pub vm_initialization: DependentCost, } +/// Gas costs for every op. +/// The difference with [`GasCostsValuesV4`]: +/// - Added `ecop` and `epar` instructions +#[allow(missing_docs)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] +#[serde(default = "GasCostsValuesV5::unit")] +pub struct GasCostsValuesV5 { + pub add: Word, + pub addi: Word, + pub and: Word, + pub andi: Word, + pub bal: Word, + pub bhei: Word, + pub bhsh: Word, + pub burn: Word, + pub cb: Word, + pub cfsi: Word, + pub div: Word, + pub divi: Word, + pub eck1: Word, + pub ecr1: Word, + pub eq: Word, + pub exp: Word, + pub expi: Word, + pub flag: Word, + pub gm: Word, + pub gt: Word, + pub gtf: Word, + pub ji: Word, + pub jmp: Word, + pub jne: Word, + pub jnei: Word, + pub jnzi: Word, + pub jmpf: Word, + pub jmpb: Word, + pub jnzf: Word, + pub jnzb: Word, + pub jnef: Word, + pub jneb: Word, + pub lb: Word, + pub log: Word, + pub lt: Word, + pub lw: Word, + pub mint: Word, + pub mlog: Word, + #[serde(rename = "mod")] + pub mod_op: Word, + pub modi: Word, + #[serde(rename = "move")] + pub move_op: Word, + pub movi: Word, + pub mroo: Word, + pub mul: Word, + pub muli: Word, + pub mldv: Word, + pub noop: Word, + pub not: Word, + pub or: Word, + pub ori: Word, + pub poph: Word, + pub popl: Word, + pub pshh: Word, + pub pshl: Word, + #[serde(rename = "ret_contract")] + pub ret: Word, + #[serde(rename = "rvrt_contract")] + pub rvrt: Word, + pub sb: Word, + pub sll: Word, + pub slli: Word, + pub srl: Word, + pub srli: Word, + pub srw: Word, + pub sub: Word, + pub subi: Word, + pub sw: Word, + pub sww: Word, + pub time: Word, + pub tr: Word, + pub tro: Word, + pub wdcm: Word, + pub wqcm: Word, + pub wdop: Word, + pub wqop: Word, + pub wdml: Word, + pub wqml: Word, + pub wddv: Word, + pub wqdv: Word, + pub wdmd: Word, + pub wqmd: Word, + pub wdam: Word, + pub wqam: Word, + pub wdmm: Word, + pub wqmm: Word, + pub xor: Word, + pub xori: Word, + pub ecop: Word, + + // Dependent + pub aloc: DependentCost, + pub bsiz: DependentCost, + pub bldd: DependentCost, + pub cfe: DependentCost, + pub cfei: DependentCost, + pub call: DependentCost, + pub ccp: DependentCost, + pub croo: DependentCost, + pub csiz: DependentCost, + pub ed19: DependentCost, + pub k256: DependentCost, + pub ldc: DependentCost, + pub logd: DependentCost, + pub mcl: DependentCost, + pub mcli: DependentCost, + pub mcp: DependentCost, + pub mcpi: DependentCost, + pub meq: DependentCost, + #[serde(rename = "retd_contract")] + pub retd: DependentCost, + pub s256: DependentCost, + pub scwq: DependentCost, + pub smo: DependentCost, + pub srwq: DependentCost, + pub swwq: DependentCost, + pub epar: DependentCost, + + // Non-opcode costs + pub contract_root: DependentCost, + pub state_root: DependentCost, + pub new_storage_per_byte: Word, + pub vm_initialization: DependentCost, +} + /// Dependent cost is a cost that depends on the number of units. #[derive( Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, @@ -1695,12 +1963,12 @@ impl GasCosts { impl GasCostsValues { /// Create costs that are all set to zero. pub fn free() -> Self { - GasCostsValuesV4::free().into() + GasCostsValuesV5::free().into() } /// Create costs that are all set to one. pub fn unit() -> Self { - GasCostsValuesV4::unit().into() + GasCostsValuesV5::unit().into() } } @@ -2664,6 +2932,254 @@ impl GasCostsValuesV4 { } } +impl GasCostsValuesV5 { + /// Create costs that are all set to zero. + pub fn free() -> Self { + Self { + add: 0, + addi: 0, + and: 0, + andi: 0, + bal: 0, + bhei: 0, + bhsh: 0, + burn: 0, + cb: 0, + cfsi: 0, + div: 0, + divi: 0, + eck1: 0, + ecr1: 0, + eq: 0, + exp: 0, + expi: 0, + flag: 0, + gm: 0, + gt: 0, + gtf: 0, + ji: 0, + jmp: 0, + jne: 0, + jnei: 0, + jnzi: 0, + jmpf: 0, + jmpb: 0, + jnzf: 0, + jnzb: 0, + jnef: 0, + jneb: 0, + lb: 0, + log: 0, + lt: 0, + lw: 0, + mint: 0, + mlog: 0, + mod_op: 0, + modi: 0, + move_op: 0, + movi: 0, + mroo: 0, + mul: 0, + muli: 0, + mldv: 0, + noop: 0, + not: 0, + or: 0, + ori: 0, + poph: 0, + popl: 0, + pshh: 0, + pshl: 0, + ret: 0, + rvrt: 0, + sb: 0, + sll: 0, + slli: 0, + srl: 0, + srli: 0, + srw: 0, + sub: 0, + subi: 0, + sw: 0, + sww: 0, + time: 0, + tr: 0, + tro: 0, + wdcm: 0, + wqcm: 0, + wdop: 0, + wqop: 0, + wdml: 0, + wqml: 0, + wddv: 0, + wqdv: 0, + wdmd: 0, + wqmd: 0, + wdam: 0, + wqam: 0, + wdmm: 0, + wqmm: 0, + xor: 0, + xori: 0, + ecop: 0, + aloc: DependentCost::free(), + bsiz: DependentCost::free(), + bldd: DependentCost::free(), + cfe: DependentCost::free(), + cfei: DependentCost::free(), + call: DependentCost::free(), + ccp: DependentCost::free(), + croo: DependentCost::free(), + csiz: DependentCost::free(), + ed19: DependentCost::free(), + k256: DependentCost::free(), + ldc: DependentCost::free(), + logd: DependentCost::free(), + mcl: DependentCost::free(), + mcli: DependentCost::free(), + mcp: DependentCost::free(), + mcpi: DependentCost::free(), + meq: DependentCost::free(), + retd: DependentCost::free(), + s256: DependentCost::free(), + scwq: DependentCost::free(), + smo: DependentCost::free(), + srwq: DependentCost::free(), + swwq: DependentCost::free(), + epar: DependentCost::free(), + + // Non-opcode costs + contract_root: DependentCost::free(), + state_root: DependentCost::free(), + new_storage_per_byte: 0, + vm_initialization: DependentCost::free(), + } + } + + /// Create costs that are all set to one. + pub fn unit() -> Self { + Self { + add: 1, + addi: 1, + and: 1, + andi: 1, + bal: 1, + bhei: 1, + bhsh: 1, + burn: 1, + cb: 1, + cfsi: 1, + div: 1, + divi: 1, + eck1: 1, + ecr1: 1, + eq: 1, + exp: 1, + expi: 1, + flag: 1, + gm: 1, + gt: 1, + gtf: 1, + ji: 1, + jmp: 1, + jne: 1, + jnei: 1, + jnzi: 1, + jmpf: 1, + jmpb: 1, + jnzf: 1, + jnzb: 1, + jnef: 1, + jneb: 1, + lb: 1, + log: 1, + lt: 1, + lw: 1, + mint: 1, + mlog: 1, + mod_op: 1, + modi: 1, + move_op: 1, + movi: 1, + mroo: 1, + mul: 1, + muli: 1, + mldv: 1, + noop: 1, + not: 1, + or: 1, + ori: 1, + ret: 1, + poph: 1, + popl: 1, + pshh: 1, + pshl: 1, + rvrt: 1, + sb: 1, + sll: 1, + slli: 1, + srl: 1, + srli: 1, + srw: 1, + sub: 1, + subi: 1, + sw: 1, + sww: 1, + time: 1, + tr: 1, + tro: 1, + wdcm: 1, + wqcm: 1, + wdop: 1, + wqop: 1, + wdml: 1, + wqml: 1, + wddv: 1, + wqdv: 1, + wdmd: 1, + wqmd: 1, + wdam: 1, + wqam: 1, + wdmm: 1, + wqmm: 1, + xor: 1, + xori: 1, + ecop: 1, + aloc: DependentCost::unit(), + bsiz: DependentCost::unit(), + bldd: DependentCost::unit(), + cfe: DependentCost::unit(), + cfei: DependentCost::unit(), + call: DependentCost::unit(), + ccp: DependentCost::unit(), + croo: DependentCost::unit(), + csiz: DependentCost::unit(), + ed19: DependentCost::unit(), + k256: DependentCost::unit(), + ldc: DependentCost::unit(), + logd: DependentCost::unit(), + mcl: DependentCost::unit(), + mcli: DependentCost::unit(), + mcp: DependentCost::unit(), + mcpi: DependentCost::unit(), + meq: DependentCost::unit(), + retd: DependentCost::unit(), + s256: DependentCost::unit(), + scwq: DependentCost::unit(), + smo: DependentCost::unit(), + srwq: DependentCost::unit(), + swwq: DependentCost::unit(), + epar: DependentCost::unit(), + + // Non-opcode costs + contract_root: DependentCost::unit(), + state_root: DependentCost::unit(), + new_storage_per_byte: 1, + vm_initialization: DependentCost::unit(), + } + } +} + impl DependentCost { /// Create costs that make operations free. pub fn free() -> Self { @@ -2784,6 +3300,12 @@ impl From for GasCostsValues { } } +impl From for GasCostsValues { + fn from(i: GasCostsValuesV5) -> Self { + GasCostsValues::V5(i) + } +} + #[cfg(test)] mod tests { use crate::DependentCost; diff --git a/fuel-tx/src/transaction/consensus_parameters/gas/default_gas_costs.rs b/fuel-tx/src/transaction/consensus_parameters/gas/default_gas_costs.rs index 7f1b19e380..732d1ccaa9 100644 --- a/fuel-tx/src/transaction/consensus_parameters/gas/default_gas_costs.rs +++ b/fuel-tx/src/transaction/consensus_parameters/gas/default_gas_costs.rs @@ -4,7 +4,7 @@ use super::*; pub const GIT: &str = "98341e564b75d1157e61d7d5f38612f6224a5b30"; /// Modified manually afterwards in https://github.com/FuelLabs/fuel-vm/pull/780 pub fn default_gas_costs() -> GasCostsValues { - GasCostsValuesV4 { + GasCostsValuesV5 { add: 1, addi: 1, and: 1, @@ -89,6 +89,11 @@ pub fn default_gas_costs() -> GasCostsValues { wqmm: 3, xor: 1, xori: 1, + ecop: 3500, + epar: DependentCost::HeavyOperation { + base: 69000, + gas_per_unit: 52000, + }, aloc: DependentCost::LightOperation { base: 2, units_per_gas: 214, diff --git a/fuel-vm/Cargo.toml b/fuel-vm/Cargo.toml index b6557e7530..dbcde9253c 100644 --- a/fuel-vm/Cargo.toml +++ b/fuel-vm/Cargo.toml @@ -20,6 +20,7 @@ anyhow = { version = "1.0", optional = true } async-trait = "0.1" backtrace = { version = "0.3", optional = true } # requires debug symbols to work bitflags = { workspace = true } +bn = { package = "substrate-bn", version = "0.6", default-features = false } derivative = "2.2" derive_more = { version = "0.99", default-features = false, features = [ "display", @@ -62,6 +63,7 @@ fuel-vm = { path = ".", default-features = false, features = [ "random", ] } futures = "0.3.28" +hex = "0.4.3" ntest = "0.9.2" num-integer = "0.1.45" p256 = "0.13" diff --git a/fuel-vm/src/interpreter/crypto.rs b/fuel-vm/src/interpreter/crypto.rs index e1ff2cf824..3732e92ce8 100644 --- a/fuel-vm/src/interpreter/crypto.rs +++ b/fuel-vm/src/interpreter/crypto.rs @@ -15,6 +15,18 @@ use crate::{ error::SimpleResult, }; +use alloc::vec::Vec; +use bn::{ + AffineG1, + AffineG2, + Fq, + Fq2, + Fr, + Group, + Gt, + G1, + G2, +}; use fuel_crypto::{ Hasher, Message, @@ -24,6 +36,7 @@ use fuel_crypto::{ use fuel_types::{ Bytes32, Bytes64, + RegisterId, Word, }; @@ -91,6 +104,37 @@ where c, ) } + + pub(crate) fn ec_operation( + &mut self, + a: Word, + b: Word, + c: Word, + d: Word, + ) -> SimpleResult<()> { + let owner = self.ownership_registers(); + ec_operation( + self.memory.as_mut(), + owner, + self.registers.pc_mut(), + a, + b, + c, + d, + ) + } + + pub(crate) fn ec_pairing( + &mut self, + ra: RegisterId, + b: Word, + c: Word, + d: Word, + ) -> SimpleResult<()> { + let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers); + let dest = &mut w[ra.try_into()?]; + ec_pairing(self.memory.as_mut(), pc, dest, b, c, d) + } } pub(crate) fn secp256k1_recover( @@ -202,3 +246,156 @@ pub(crate) fn sha256( memory.write_bytes(owner, a, *Hasher::hash(memory.read(b, c)?))?; Ok(inc_pc(pc)?) } + +fn read_g1_point_alt_bn_128( + memory: &MemoryInstance, + point_ptr: Word, +) -> SimpleResult { + // Big endian required by the library + let arg_bytes: [u8; 2 * 32] = memory.read_bytes(point_ptr)?; + + let px = Fq::from_slice(&arg_bytes[..32]) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + let py = Fq::from_slice(&arg_bytes[32..64]) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + + Ok(if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py) + .map(Into::into) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)? + }) +} + +fn read_g2_point_alt_bn_128( + memory: &MemoryInstance, + point_ptr: Word, +) -> SimpleResult { + // Big endian required by the library + let arg_bytes: [u8; 4 * 32] = memory.read_bytes(point_ptr)?; + + let ay = Fq::from_slice(&arg_bytes[..32]) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + let ax = Fq::from_slice(&arg_bytes[32..64]) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + let by = Fq::from_slice(&arg_bytes[64..96]) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + let bx = Fq::from_slice(&arg_bytes[96..128]) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + + let a = Fq2::new(ax, ay); + let b = Fq2::new(bx, by); + Ok(if a.is_zero() && b.is_zero() { + G2::zero() + } else { + G2::from( + AffineG2::new(a, b) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?, + ) + }) +} + +pub(crate) fn ec_operation( + memory: &mut MemoryInstance, + owner: OwnershipRegisters, + pc: RegMut, + dst: Word, + curve_id: Word, + operation_type: Word, + points_ptr: Word, +) -> SimpleResult<()> { + match curve_id { + 0 => { + match operation_type { + // Two points addition + 0 => { + let point1 = read_g1_point_alt_bn_128(memory, points_ptr)?; + let point2 = read_g1_point_alt_bn_128( + memory, + points_ptr + .checked_add(64) + .ok_or(fuel_tx::PanicReason::MemoryOverflow)?, + )?; + let mut output = [0u8; 64]; + // SAFETY: The library override the addition and is tested and + // audited. Here is the code of the addition : + // https://github.com/paritytech/bn/blob/63f8c587356a67b33c7396af98e065b66fca5dda/src/groups/mod.rs#L297 + #[allow(clippy::arithmetic_side_effects)] + if let Some(sum) = AffineG1::from_jacobian(point1 + point2) { + sum.x().to_big_endian(&mut output[..32]).unwrap(); + sum.y().to_big_endian(&mut output[32..]).unwrap(); + } + memory.write_bytes(owner, dst, output)?; + } + // Scalar multiplication + 1 => { + let point = read_g1_point_alt_bn_128(memory, points_ptr)?; + let scalar = Fr::from_slice( + memory.read( + points_ptr + .checked_add(64) + .ok_or(fuel_tx::PanicReason::MemoryOverflow)?, + 32u64, + )?, + ) + .map_err(|_| fuel_tx::PanicReason::InvalidEllipticCurvePoint)?; + let mut output = [0u8; 64]; + // SAFETY: The library override the multiplication and is tested and + // audited. Here is the code of the multiplication + // : https://github.com/paritytech/bn/blob/63f8c587356a67b33c7396af98e065b66fca5dda/src/groups/mod.rs#L275 + #[allow(clippy::arithmetic_side_effects)] + if let Some(product) = AffineG1::from_jacobian(point * scalar) { + product.x().to_big_endian(&mut output[..32]).unwrap(); + product.y().to_big_endian(&mut output[32..]).unwrap(); + } + memory.write_bytes(owner, dst, output)?; + } + _ => return Err(fuel_tx::PanicReason::UnsupportedOperationType.into()), + } + } + _ => return Err(fuel_tx::PanicReason::UnsupportedCurveId.into()), + } + Ok(inc_pc(pc)?) +} + +pub(crate) fn ec_pairing( + memory: &mut MemoryInstance, + pc: RegMut, + success: &mut u64, + identifier: Word, + number_elements: Word, + elements_ptr: Word, +) -> SimpleResult<()> { + match identifier { + // Optimal ate pairing / alt_bn128 + 0 => { + // Each element consists of an uncompressed G1 point (64 bytes) and an + // uncompressed G2 point (128 bytes). + let element_size = 128 + 64; + let mut elements = Vec::with_capacity( + usize::try_from(number_elements) + .map_err(|_| fuel_tx::PanicReason::MemoryOverflow)?, + ); + for idx in 0..number_elements { + let start_offset = elements_ptr + .checked_add( + idx.checked_mul(element_size) + .ok_or(fuel_tx::PanicReason::MemoryOverflow)?, + ) + .ok_or(fuel_tx::PanicReason::MemoryOverflow)?; + let a = read_g1_point_alt_bn_128(memory, start_offset)?; + let b = read_g2_point_alt_bn_128( + memory, + start_offset + .checked_add(64) + .ok_or(fuel_tx::PanicReason::MemoryOverflow)?, + )?; + elements.push((a, b)); + } + *success = (bn::pairing_batch(&elements) == Gt::one()) as u64; + } + _ => return Err(fuel_tx::PanicReason::UnsupportedOperationType.into()), + } + Ok(inc_pc(pc)?) +} diff --git a/fuel-vm/src/interpreter/crypto/tests.rs b/fuel-vm/src/interpreter/crypto/tests.rs index c5634d62d8..acdc919a5a 100644 --- a/fuel-vm/src/interpreter/crypto/tests.rs +++ b/fuel-vm/src/interpreter/crypto/tests.rs @@ -7,6 +7,9 @@ use rand::{ RngCore, SeedableRng, }; +use rstest::rstest; + +use crate::consts::VM_MAX_RAM; use super::*; use fuel_vm::consts::*; @@ -193,3 +196,625 @@ fn test_sha256() -> SimpleResult<()> { assert_ne!(&memory[hash as usize..hash as usize + 32], &[1u8; 32][..]); Ok(()) } + +#[rstest] +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[case( + hex::decode( + "\ + 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\ + 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\ + 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\ + 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + ).unwrap(), + hex::decode( + "\ + 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\ + 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + ).unwrap() +)] +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[case( + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ).unwrap(), + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap() +)] +// From https://github.com/ethereum/tests/blob/develop/GeneralStateTests/stZeroKnowledge2/ecadd_1145-3932_2969-1336_21000_128.json +#[case( + hex::decode( + "\ + 17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa9\ + 01e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c\ + 039730ea8dff1254c0fee9c0ea777d29a9c710b7e616683f194f18c43b43b869\ + 073a5ffcc6fc7a28c30723d6e58ce577356982d65b833a5a5c15bf9024b43d98", + ).unwrap(), + hex::decode( + "\ + 15bf2bb17880144b5d1cd2b1f46eff9d617bffd1ca57c37fb5a49bd84e53cf66\ + 049c797f9ce0d17083deb32b5e36f2ea2a212ee036598dd7624c168993d1355f", + ).unwrap() +)] +// From https://github.com/matter-labs/era-compiler-tests/blob/2253941334797eb2a997941845fb9eb0d436558b/yul/precompiles/ecadd.yul#L123 +#[case( + hex::decode( + "\ + 17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa9\ + 01e0559bacb160664764a357af8a9fe70baa9258e0b959273ffc5718c6d4cc7c\ + 17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa9\ + 2e83f8d734803fc370eba25ed1f6b8768bd6d83887b87165fc2434fe11a830cb", + ).unwrap(), + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ).unwrap() +)] +// From https://github.com/poanetwork/parity-ethereum/blob/2ea4265b0083c4148571b21e1079c641d5f31dc2/ethcore/benches/builtin.rs#L486 +#[case( + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002", + ).unwrap(), + hex::decode( + "\ + 030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd3\ + 15ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4", + ).unwrap() +)] +fn test_ecop_addition( + #[case] input: Vec, + #[case] expected: Vec, +) -> SimpleResult<()> { + // Given + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1000, + hp: 2000, + prev_hp: VM_MAX_RAM - 1, + }; + let mut pc = 4; + let points_address = 0; + let result = 2100u64; + // P1(x,y),P2(x,y) + memory[points_address..points_address + 128].copy_from_slice(&input); + + // When + ec_operation( + &mut memory, + owner, + RegMut::new(&mut pc), + result as Word, + 0, + 0, + points_address as Word, + )?; + + // Then + assert_eq!(pc, 8); + assert_eq!( + &memory[result as usize..result.checked_add(64).unwrap() as usize], + &expected + ); + Ok(()) +} + +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[test] +fn test_ecop_addition_error() -> SimpleResult<()> { + // Given + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1000, + hp: 2000, + prev_hp: VM_MAX_RAM - 1, + }; + let mut pc = 4; + let points_address = 0; + let result = 2100u64; + // P1(x,y),P2(x,y) + memory[points_address..points_address + 128].copy_from_slice(&input); + + // When + let err = ec_operation( + &mut memory, + owner, + RegMut::new(&mut pc), + result as Word, + 0, + 0, + points_address as Word, + ) + .unwrap_err(); + + // Then + assert_eq!( + err, + crate::error::PanicOrBug::Panic(fuel_tx::PanicReason::InvalidEllipticCurvePoint) + ); + Ok(()) +} + +#[rstest] +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[case( + hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2", + ) + .unwrap(), + hex::decode( + "\ + 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ + 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + ).unwrap() +)] +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[case( + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0200000000000000000000000000000000000000000000000000000000000000" + ).unwrap(), + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap() +)] +// From https://github.com/ethereum/tests/blob/develop/GeneralStateTests/stZeroKnowledge/ecmul_7827-6598_1456_21000_96.json +#[case( + hex::decode( + "\ + 1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3\ + 1a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6\ + 0000000000000000000000000000000100000000000000000000000000000000", + ).unwrap(), + hex::decode( + "\ + 1051acb0700ec6d42a88215852d582efbaef31529b6fcbc3277b5c1b300f5cf0\ + 135b2394bb45ab04b8bd7611bd2dfe1de6a4e6e2ccea1ea1955f577cd66af85b", + ).unwrap() +)] +// From https://github.com/matter-labs/era-compiler-tests/blob/2253941334797eb2a997941845fb9eb0d436558b/yul/precompiles/ecmul.yul#L185C21-L185C98 +#[case( + hex::decode( + "\ + 1a87b0584ce92f4593d161480614f2989035225609f08058ccfa3d0f940febe3\ + 1a2f3c951f6dadcc7ee9007dff81504b0fcd6d7cf59996efdc33d92bf7f9f8f6\ + 30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" + ).unwrap(), + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000000\ + 0000000000000000000000000000000000000000000000000000000000000000", + ).unwrap() +)] +// From https://github.com/poanetwork/parity-ethereum/blob/2ea4265b0083c4148571b21e1079c641d5f31dc2/ethcore/benches/builtin.rs#L516 +#[case( + hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2" + ).unwrap(), + hex::decode( + "\ + 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ + 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc" + ).unwrap() +)] +fn test_ecop_multiplication( + #[case] input: Vec, + #[case] expected: Vec, +) -> SimpleResult<()> { + // Given + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1000, + hp: 2000, + prev_hp: VM_MAX_RAM - 1, + }; + let mut pc = 4; + let points_address = 0; + let result = 2100u64; + + // P1(x,y),scalar + memory[points_address..points_address + 96].copy_from_slice(&input); + + // When + ec_operation( + &mut memory, + owner, + RegMut::new(&mut pc), + result as Word, + 0, + 1, + points_address as Word, + )?; + + // Then + assert_eq!(pc, 8); + assert_eq!( + &memory[result as usize..result.checked_add(64).unwrap() as usize], + &expected + ); + Ok(()) +} + +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[test] +fn test_ecop_multiplication_error() -> SimpleResult<()> { + // Given + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 0f00000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(); + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1000, + hp: 2000, + prev_hp: VM_MAX_RAM - 1, + }; + let mut pc = 4; + let points_address = 0; + let result = 2100u64; + // P1(x,y),scalar + memory[points_address..points_address + 96].copy_from_slice(&input); + + // When + let err = ec_operation( + &mut memory, + owner, + RegMut::new(&mut pc), + result as Word, + 0, + 1, + points_address as Word, + ) + .unwrap_err(); + + // Then + assert_eq!( + err, + crate::error::PanicOrBug::Panic(fuel_tx::PanicReason::InvalidEllipticCurvePoint) + ); + Ok(()) +} + +#[test] +fn test_ecop_read_memory_not_accessible() -> SimpleResult<()> { + // Given + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1000, + hp: 2000, + prev_hp: VM_MAX_RAM - 1, + }; + let mut pc = 4; + let points_address = VM_MAX_RAM; + let result = 2100u64; + + // When + let err = ec_operation( + &mut memory, + owner, + RegMut::new(&mut pc), + result as Word, + 0, + 1, + points_address as Word, + ) + .unwrap_err(); + + // Then + assert_eq!( + err, + crate::error::PanicOrBug::Panic(fuel_tx::PanicReason::MemoryOverflow) + ); + Ok(()) +} + +#[test] +fn test_ecop_write_memory_not_accessible() -> SimpleResult<()> { + // Given + let input = hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2", + ) + .unwrap(); + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let owner = OwnershipRegisters { + sp: 1000, + ssp: 1000, + hp: 2000, + prev_hp: VM_MAX_RAM - 1, + }; + let mut pc = 4; + let points_address = 0; + let result = 0u64; + // P1(x,y),scalar + memory[points_address..points_address + 96].copy_from_slice(&input); + + // When + let err = ec_operation( + &mut memory, + owner, + RegMut::new(&mut pc), + result as Word, + 0, + 1, + points_address as Word, + ) + .unwrap_err(); + + // Then + assert_eq!( + err, + crate::error::PanicOrBug::Panic(fuel_tx::PanicReason::MemoryOwnership) + ); + Ok(()) +} + +#[rstest] +// From https://github.com/bluealloy/revm/blob/main/crates/precompile/src/bn128.rs +#[case( + hex::decode( + "\ + 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ + 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ + 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ + 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ + 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ + 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ + 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ + 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ).unwrap(), + 1u64 +)] +// From https://github.com/ethereum/tests/blob/develop/GeneralStateTests/stZeroKnowledge/ecpairing_three_point_match_1.json +#[case( + hex::decode( + "\ + 105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf1016\ + 0cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc\ + 0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd\ + 3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1\ + 14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21\ + 01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 1a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7\ + 290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f\ + 2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb9\ + 29d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75" + ).unwrap(), + 1u64 +)] +// From https://github.com/ethereum/tests/blob/develop/GeneralStateTests/stZeroKnowledge/ecpairing_three_point_fail_1.json +#[case( + hex::decode( + "\ + 105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf1016\ + 0cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc\ + 0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd\ + 3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1\ + 14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21\ + 01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 1a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7\ + 290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f\ + 00cacf3523caf879d7d05e30549f1e6fdce364cbb8724b0329c6c2a39d4f018e\ + 0692e55db067300e6e3fe56218fa2f940054e57e7ef92bf7d475a9d8a8502fd2" + ).unwrap(), + 0u64 +)] +// From https://github.com/poanetwork/parity-ethereum/blob/2ea4265b0083c4148571b21e1079c641d5f31dc2/ethcore/benches/builtin.rs#L686 +#[case( + hex::decode( + "\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec\ + 1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec\ + 1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec\ + 1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec\ + 1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000002\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec\ + 1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d" + ).unwrap(), + 1u64 +)] +fn test_epar(#[case] input: Vec, #[case] expected: u64) -> SimpleResult<()> { + // Given + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let mut pc = 4; + let points_address: usize = 0; + let mut result = 0; + + // Length + let nb_elements = input + .len() + .checked_div(128usize.checked_add(64).unwrap()) + .unwrap(); + // P1(x,y),G2(p1(x,y), p2(x,y)) + memory[points_address..points_address.checked_add(input.len()).unwrap()] + .copy_from_slice(&input); + + // When + ec_pairing( + &mut memory, + RegMut::new(&mut pc), + &mut result, + 0, + nb_elements as Word, + 0 as Word, + )?; + + // Then + assert_eq!(pc, 8); + assert_eq!(result, expected); + Ok(()) +} + +#[test] +fn test_epar_error() -> SimpleResult<()> { + // Given + let input = hex::decode( + "\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111\ + 1111111111111111111111111111111111111111111111111111111111111111", + ) + .unwrap(); + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let mut pc = 4; + let points_address = 0; + let mut result = 0; + // Length + let nb_elements = input + .len() + .checked_div(128usize.checked_add(64).unwrap()) + .unwrap(); + // P1(x,y),G2(p1(x,y), p2(x,y)) + memory[points_address..points_address + 192].copy_from_slice(&input); + + // When + let err = ec_pairing( + &mut memory, + RegMut::new(&mut pc), + &mut result, + 0, + nb_elements as Word, + 0 as Word, + ) + .unwrap_err(); + + // Then + assert_eq!( + err, + crate::error::PanicOrBug::Panic(fuel_tx::PanicReason::InvalidEllipticCurvePoint) + ); + Ok(()) +} + +#[test] +fn test_epar_read_memory_not_accessible() -> SimpleResult<()> { + // Given + let mut memory: MemoryInstance = vec![1u8; MEM_SIZE].try_into().unwrap(); + let mut pc = 4; + let points_address = VM_MAX_RAM; + let mut result = 0; + // Length + let nb_elements = 1u64; + // When + let err = ec_pairing( + &mut memory, + RegMut::new(&mut pc), + &mut result, + 0, + nb_elements as Word, + points_address as Word, + ) + .unwrap_err(); + + // Then + assert_eq!( + err, + crate::error::PanicOrBug::Panic(fuel_tx::PanicReason::MemoryOverflow) + ); + Ok(()) +} diff --git a/fuel-vm/src/interpreter/executors/instruction.rs b/fuel-vm/src/interpreter/executors/instruction.rs index bc7c7b8cfa..64737bccc3 100644 --- a/fuel-vm/src/interpreter/executors/instruction.rs +++ b/fuel-vm/src/interpreter/executors/instruction.rs @@ -917,6 +917,22 @@ where let (a, b, c, d) = bldd.unpack(); self.blob_load_data(r!(a), r!(b), r!(c), r!(d))?; } + + Instruction::ECOP(ecop) => { + self.gas_charge(self.gas_costs().ecop().map_err(PanicReason::from)?)?; + let (a, b, c, d) = ecop.unpack(); + self.ec_operation(r!(a), r!(b), r!(c), r!(d))?; + } + + Instruction::EPAR(epar) => { + let (a, b, c, d) = epar.unpack(); + let len = r!(c); + self.dependent_gas_charge( + self.gas_costs().epar().map_err(PanicReason::from)?, + len, + )?; + self.ec_pairing(a.into(), r!(b), len, r!(d))?; + } } Ok(ExecuteState::Proceed) diff --git a/fuel-vm/src/interpreter/executors/instruction/tests/reserved_registers.rs b/fuel-vm/src/interpreter/executors/instruction/tests/reserved_registers.rs index c2ac0d7e61..8b76445750 100644 --- a/fuel-vm/src/interpreter/executors/instruction/tests/reserved_registers.rs +++ b/fuel-vm/src/interpreter/executors/instruction/tests/reserved_registers.rs @@ -95,6 +95,13 @@ fn cant_write_to_reserved_registers(raw_random_instruction: u32) -> TestResult { // Some opcodes parse the immediate value as a part of the instruction itself, // and thus fail before the destination register writability check occurs. Err(Some(PanicReason::InvalidImmediateValue)) => return TestResult::discard(), + // Epar opcode parse the memory read and throw error if incorrect memory + // before changing the register + Err(Some(PanicReason::InvalidEllipticCurvePoint)) + if opcode == Opcode::EPAR => + { + return TestResult::discard(); + } _ => { return TestResult::error(format!( "expected ReservedRegisterNotWritable error {:?}", @@ -244,6 +251,8 @@ fn writes_to_ra(opcode: Opcode) -> bool { Opcode::ECAL => true, Opcode::BSIZ => true, Opcode::BLDD => false, + Opcode::ECOP => false, + Opcode::EPAR => true, } } @@ -361,5 +370,7 @@ fn writes_to_rb(opcode: Opcode) -> bool { Opcode::ECAL => true, Opcode::BSIZ => false, Opcode::BLDD => false, + Opcode::ECOP => false, + Opcode::EPAR => false, } } diff --git a/fuel-vm/src/tests/crypto.rs b/fuel-vm/src/tests/crypto.rs index 8b0397693b..b79bf58e0d 100644 --- a/fuel-vm/src/tests/crypto.rs +++ b/fuel-vm/src/tests/crypto.rs @@ -861,3 +861,225 @@ fn k256__register_b_overflows() { check_expected_reason_for_instructions(script, MemoryOverflow); } + +#[test] +fn ecop__addition__works() { + let mut client = MemoryClient::default(); + + let gas_limit = 1_000_000; + let maturity = Default::default(); + let height = Default::default(); + + // Given + #[rustfmt::skip] + let script = vec![ + // Get the points and expected result from the script data + op::gtf_args(0x10, 0x00, GTFArgs::ScriptData), + // Store the expect result pointer to 0x11 reg (add 128 (points len) to the script data pointer) + op::addi(0x11, 0x10, 0x80), + // Store 64 bytes to allocate for the result + op::movi(0x12, 0x40), + // Allocate 64 bytes for the result + op::aloc(0x12), + // Store the result pointer to 0x12 reg + op::move_(0x12, RegId::HP), + // Perform addition of the two points + op::ecop(0x12, RegId::ZERO, RegId::ZERO, 0x10), + // Store the len of result in 0x13 + op::movi(0x13, 0x40), + // Compare the result with the expected result and store 0 or 1 in 0x13 + op::meq(0x13, 0x11, 0x12, 0x13), + // Log the result of the comparison + op::log(0x13, 0x00, 0x00, 0x00), + op::ret(RegId::ONE), + ].into_iter().collect(); + + // Point 1 + Point 2 + Result + let mut script_data = Vec::new(); + // Point 1 + script_data.extend( + hex::decode( + "\ + 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\ + 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266", + ) + .unwrap(), + ); + // Point 2 + script_data.extend( + hex::decode( + "\ + 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\ + 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", + ) + .unwrap(), + ); + // Result + script_data.extend( + hex::decode( + "\ + 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\ + 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", + ) + .unwrap(), + ); + + // When + let tx = TransactionBuilder::script(script, script_data) + .script_gas_limit(gas_limit) + .maturity(maturity) + .add_fee_input() + .finalize_checked(height); + + let receipts = client.transact(tx); + + // Then + let success = receipts + .iter() + .any(|r| matches!(r, Receipt::Log{ ra, .. } if *ra == 1)); + assert!(success); +} + +#[test] +fn ecop__multiplication__works() { + let mut client = MemoryClient::default(); + + let gas_limit = 1_000_000; + let maturity = Default::default(); + let height = Default::default(); + + // Given + #[rustfmt::skip] + let script = vec![ + // Get the point, scalar and expected result from the script data + op::gtf_args(0x10, 0x00, GTFArgs::ScriptData), + // Store the expect result pointer to 0x11 reg (add 96 (point + scalar len) to the script data pointer) + op::addi(0x11, 0x10, 0x60), + // Store 64 bytes to allocate for the result + op::movi(0x12, 0x40), + // Allocate 64 bytes for the result + op::aloc(0x12), + // Store the result pointer to 0x12 reg + op::move_(0x12, RegId::HP), + // Perform multiplication of the two points + op::ecop(0x12, RegId::ZERO, RegId::ONE, 0x10), + // Store the len of result in 0x13 + op::movi(0x13, 0x40), + // Compare the result with the expected result and store 0 or 1 in 0x13 + op::meq(0x13, 0x11, 0x12, 0x13), + // Log the result of the comparison + op::log(0x13, 0x00, 0x00, 0x00), + op::ret(RegId::ONE), + ].into_iter().collect(); + + // Point 1 + Scalar + Result + let mut script_data = Vec::new(); + // Point 1 + script_data.extend( + hex::decode( + "\ + 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ + 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204", + ) + .unwrap(), + ); + // Scalar + script_data.extend( + hex::decode( + "\ + 00000000000000000000000000000000000000000000000011138ce750fa15c2", + ) + .unwrap(), + ); + // Result + script_data.extend( + hex::decode( + "\ + 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ + 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", + ) + .unwrap(), + ); + + // When + let tx = TransactionBuilder::script(script, script_data) + .script_gas_limit(gas_limit) + .maturity(maturity) + .add_fee_input() + .finalize_checked(height); + + let receipts = client.transact(tx); + + // Then + let success = receipts + .iter() + .any(|r| matches!(r, Receipt::Log{ ra, .. } if *ra == 1)); + assert!(success); +} + +#[test] +fn epar__works() { + let mut client = MemoryClient::default(); + + let gas_limit = 1_000_000; + let maturity = Default::default(); + let height = Default::default(); + + // Given + #[rustfmt::skip] + let script = vec![ + // Get the point, scalar and expected result from the script data + op::gtf_args(0x10, 0x00, GTFArgs::ScriptData), + // Store the number of batchs in 0x11 + op::movi(0x11, 0x02), + // Perform multiplication of the two points + op::epar(0x12, RegId::ZERO, 0x11, 0x10), + // Log the result of the comparison + op::log(0x12, 0x00, 0x00, 0x00), + op::ret(RegId::ONE), + ].into_iter().collect(); + + // Batch 1 + Batch 2 + let mut script_data = Vec::new(); + // Batch 1 + script_data.extend( + hex::decode( + "\ + 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ + 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ + 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ + 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ + 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ + 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550", + ) + .unwrap(), + ); + // Batch 2 + script_data.extend( + hex::decode( + "\ + 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ + 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ + 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ + 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ + 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ + 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ) + .unwrap(), + ); + + // When + let tx = TransactionBuilder::script(script, script_data) + .script_gas_limit(gas_limit) + .maturity(maturity) + .add_fee_input() + .finalize_checked(height); + + let receipts = client.transact(tx); + + // Then + let success = receipts + .iter() + .any(|r| matches!(r, Receipt::Log{ ra, .. } if *ra == 1)); + assert!(success); +} diff --git a/fuel-vm/src/tests/predicate.rs b/fuel-vm/src/tests/predicate.rs index d32f503ac7..2d4a93c2c7 100644 --- a/fuel-vm/src/tests/predicate.rs +++ b/fuel-vm/src/tests/predicate.rs @@ -38,7 +38,7 @@ use crate::{ }; use core::iter; use fuel_tx::{ - consensus_parameters::gas::GasCostsValuesV4, + consensus_parameters::gas::GasCostsValuesV5, ConsensusParameters, }; @@ -193,9 +193,9 @@ fn estimate_predicate_works_when_max_gas_per_predicate_less_than_tx_gas__10_inpu let mut script = builder.finalize(); // Given - let gas_costs = GasCostsValuesV4 { + let gas_costs = GasCostsValuesV5 { ret: MAX_PREDICATE_GAS, - ..GasCostsValuesV4::free() + ..GasCostsValuesV5::free() }; let gas_costs = GasCosts::new(gas_costs.into()); let predicate_param =