Skip to content

Commit 93835df

Browse files
obatirouenitrat
andcommitted
feat: Prague precompiles (#1462)
Closes #1451 --------- Co-authored-by: enitrat <[email protected]>
1 parent 07c7dbf commit 93835df

File tree

13 files changed

+910
-399
lines changed

13 files changed

+910
-399
lines changed

cairo/ethereum/prague/vm/gas.cairo

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@ namespace GasConstants {
6666
const GAS_PER_BLOB = 2 ** 17;
6767
const MIN_BLOB_GASPRICE = 1;
6868
const BLOB_BASE_FEE_UPDATE_FRACTION = 3338477;
69+
70+
const GAS_BLS_G1_ADD = 375;
71+
const GAS_BLS_G1_MUL = 12000;
72+
const GAS_BLS_G1_MAP = 5500;
73+
const GAS_BLS_G2_ADD = 600;
74+
const GAS_BLS_G2_MUL = 22500;
75+
const GAS_BLS_G2_MAP = 23800;
6976
}
7077

7178
struct ExtendMemory {
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//! Important: the implementations of these precompiles is unsound.
2+
//! TODO: Add rust implementations for the hints.
3+
4+
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin, ModBuiltin, PoseidonBuiltin
5+
from ethereum.prague.vm.evm_impl import Evm, EvmImpl
6+
from ethereum.exceptions import EthereumException
7+
from ethereum.prague.vm.exceptions import InvalidParameter
8+
from ethereum.utils.numeric import divmod
9+
from ethereum.prague.vm.gas import GasConstants, charge_gas
10+
from ethereum_types.numeric import Uint
11+
from ethereum_types.bytes import Bytes
12+
from cairo_core.comparison import is_zero, is_not_zero
13+
14+
// @notice The bls12_381 G1 point addition precompile.
15+
// @dev The implementation is unsound.
16+
func bls12_g1_add{
17+
range_check_ptr,
18+
bitwise_ptr: BitwiseBuiltin*,
19+
keccak_ptr: felt*,
20+
poseidon_ptr: PoseidonBuiltin*,
21+
range_check96_ptr: felt*,
22+
add_mod_ptr: ModBuiltin*,
23+
mul_mod_ptr: ModBuiltin*,
24+
evm: Evm,
25+
}() -> EthereumException* {
26+
alloc_locals;
27+
let data = evm.value.message.value.data;
28+
29+
if (data.value.len != 256) {
30+
tempvar err = new EthereumException(InvalidParameter);
31+
return err;
32+
}
33+
34+
// gas
35+
let err = charge_gas(Uint(GasConstants.GAS_BLS_G1_ADD));
36+
if (cast(err, felt) != 0) {
37+
return err;
38+
}
39+
// Operation
40+
tempvar data = data;
41+
tempvar error: EthereumException*;
42+
tempvar output: Bytes;
43+
%{ bls12_g1_add_hint %}
44+
if (cast(error, felt) != 0) {
45+
return error;
46+
}
47+
48+
EvmImpl.set_output(output);
49+
tempvar ok = cast(0, EthereumException*);
50+
return ok;
51+
}
52+
53+
// @notice The bls12_381 G1 multi-scalar multiplication precompile.
54+
// @dev The implementation is unsound.
55+
func bls12_g1_msm{
56+
range_check_ptr,
57+
bitwise_ptr: BitwiseBuiltin*,
58+
keccak_ptr: felt*,
59+
poseidon_ptr: PoseidonBuiltin*,
60+
range_check96_ptr: felt*,
61+
add_mod_ptr: ModBuiltin*,
62+
mul_mod_ptr: ModBuiltin*,
63+
evm: Evm,
64+
}() -> EthereumException* {
65+
alloc_locals;
66+
67+
let data = evm.value.message.value.data;
68+
let (q, r) = divmod(data.value.len, 160);
69+
let data_multiple_of_160 = is_not_zero(r);
70+
let data_is_zero = is_zero(data.value.len);
71+
let invalid_valid_input = data_multiple_of_160 + data_is_zero;
72+
73+
if (invalid_valid_input != 0) {
74+
tempvar err = new EthereumException(InvalidParameter);
75+
return err;
76+
}
77+
78+
tempvar data = data;
79+
tempvar gas;
80+
%{ bls12_g1_msm_gas_hint %}
81+
let err = charge_gas(Uint(gas));
82+
if (cast(err, felt) != 0) {
83+
return err;
84+
}
85+
86+
tempvar data = evm.value.message.value.data;
87+
tempvar error: EthereumException*;
88+
tempvar output: Bytes;
89+
90+
%{ bls12_g1_msm_hint %}
91+
if (cast(error, felt) != 0) {
92+
return error;
93+
}
94+
95+
EvmImpl.set_output(output);
96+
tempvar ok = cast(0, EthereumException*);
97+
return ok;
98+
}
99+
100+
// @notice Precompile to map field element to G1.
101+
// @dev The implementation is unsound.
102+
func bls12_map_fp_to_g1{
103+
range_check_ptr,
104+
bitwise_ptr: BitwiseBuiltin*,
105+
keccak_ptr: felt*,
106+
poseidon_ptr: PoseidonBuiltin*,
107+
evm: Evm,
108+
}() -> EthereumException* {
109+
alloc_locals;
110+
111+
let data = evm.value.message.value.data;
112+
if (data.value.len != 64) {
113+
tempvar err = new EthereumException(InvalidParameter);
114+
return err;
115+
}
116+
117+
// gas
118+
let err = charge_gas(Uint(GasConstants.GAS_BLS_G1_MAP));
119+
if (cast(err, felt) != 0) {
120+
return err;
121+
}
122+
123+
// operation
124+
tempvar data = data;
125+
tempvar error: EthereumException*;
126+
tempvar output: Bytes;
127+
128+
%{ bls12_map_fp_to_g1_hint %}
129+
if (cast(error, felt) != 0) {
130+
return error;
131+
}
132+
133+
EvmImpl.set_output(output);
134+
tempvar ok = cast(0, EthereumException*);
135+
return ok;
136+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//! Important: the implementations of these precompiles is unsound.
2+
//! TODO: Add rust implementations for the hints.
3+
4+
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin, ModBuiltin, PoseidonBuiltin
5+
from ethereum.prague.vm.evm_impl import Evm, EvmImpl
6+
from ethereum.exceptions import EthereumException
7+
from ethereum.prague.vm.exceptions import InvalidParameter
8+
from ethereum.utils.numeric import divmod
9+
from ethereum.prague.vm.gas import GasConstants, charge_gas
10+
from ethereum_types.numeric import Uint
11+
from ethereum_types.bytes import Bytes
12+
from cairo_core.comparison import is_zero, is_not_zero
13+
14+
// @notice The bls12_381 G2 point addition precompile.
15+
// @dev The implementation is unsound.
16+
func bls12_g2_add{
17+
range_check_ptr,
18+
bitwise_ptr: BitwiseBuiltin*,
19+
keccak_ptr: felt*,
20+
poseidon_ptr: PoseidonBuiltin*,
21+
range_check96_ptr: felt*,
22+
add_mod_ptr: ModBuiltin*,
23+
mul_mod_ptr: ModBuiltin*,
24+
evm: Evm,
25+
}() -> EthereumException* {
26+
alloc_locals;
27+
let data = evm.value.message.value.data;
28+
29+
if (data.value.len != 512) {
30+
tempvar err = new EthereumException(InvalidParameter);
31+
return err;
32+
}
33+
34+
// gas
35+
let err = charge_gas(Uint(GasConstants.GAS_BLS_G2_ADD));
36+
if (cast(err, felt) != 0) {
37+
return err;
38+
}
39+
// Operation
40+
tempvar data = data;
41+
tempvar error: EthereumException*;
42+
tempvar output: Bytes;
43+
%{ bls12_g2_add_hint %}
44+
if (cast(error, felt) != 0) {
45+
return error;
46+
}
47+
48+
EvmImpl.set_output(output);
49+
tempvar ok = cast(0, EthereumException*);
50+
return ok;
51+
}
52+
53+
// @notice The bls12_381 G2 multi-scalar multiplication precompile.
54+
// @dev The implementation is unsound.
55+
func bls12_g2_msm{
56+
range_check_ptr,
57+
bitwise_ptr: BitwiseBuiltin*,
58+
keccak_ptr: felt*,
59+
poseidon_ptr: PoseidonBuiltin*,
60+
range_check96_ptr: felt*,
61+
add_mod_ptr: ModBuiltin*,
62+
mul_mod_ptr: ModBuiltin*,
63+
evm: Evm,
64+
}() -> EthereumException* {
65+
alloc_locals;
66+
67+
let data = evm.value.message.value.data;
68+
let (q, r) = divmod(data.value.len, 288);
69+
let data_multiple_of_288 = is_not_zero(r);
70+
let data_is_zero = is_zero(data.value.len);
71+
let invalid_valid_input = data_multiple_of_288 + data_is_zero;
72+
73+
if (invalid_valid_input != 0) {
74+
tempvar err = new EthereumException(InvalidParameter);
75+
return err;
76+
}
77+
78+
tempvar data = data;
79+
tempvar gas;
80+
%{ bls12_g2_msm_gas_hint %}
81+
let err = charge_gas(Uint(gas));
82+
if (cast(err, felt) != 0) {
83+
return err;
84+
}
85+
86+
tempvar data = evm.value.message.value.data;
87+
tempvar error: EthereumException*;
88+
tempvar output: Bytes;
89+
90+
%{ bls12_g2_msm_hint %}
91+
if (cast(error, felt) != 0) {
92+
return error;
93+
}
94+
95+
EvmImpl.set_output(output);
96+
tempvar ok = cast(0, EthereumException*);
97+
return ok;
98+
}
99+
100+
// @notice Precompile to map field element to G2.
101+
// @dev The implementation is unsound.
102+
func bls12_map_fp2_to_g2{
103+
range_check_ptr,
104+
bitwise_ptr: BitwiseBuiltin*,
105+
keccak_ptr: felt*,
106+
poseidon_ptr: PoseidonBuiltin*,
107+
evm: Evm,
108+
}() -> EthereumException* {
109+
alloc_locals;
110+
111+
let data = evm.value.message.value.data;
112+
if (data.value.len != 64) {
113+
tempvar err = new EthereumException(InvalidParameter);
114+
return err;
115+
}
116+
117+
// gas
118+
let err = charge_gas(Uint(GasConstants.GAS_BLS_G2_MAP));
119+
if (cast(err, felt) != 0) {
120+
return err;
121+
}
122+
123+
// operation
124+
tempvar data = data;
125+
tempvar error: EthereumException*;
126+
tempvar output: Bytes;
127+
128+
%{ bls12_map_fp2_to_g2_hint %}
129+
if (cast(error, felt) != 0) {
130+
return error;
131+
}
132+
133+
EvmImpl.set_output(output);
134+
tempvar ok = cast(0, EthereumException*);
135+
return ok;
136+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! Important: the implementations of these precompiles is unsound.
2+
//TODO: Add rust implementations for the hints.
3+
4+
from starkware.cairo.common.cairo_builtins import BitwiseBuiltin, ModBuiltin, PoseidonBuiltin
5+
from ethereum.prague.vm.evm_impl import Evm, EvmImpl
6+
from ethereum.exceptions import EthereumException
7+
from ethereum.prague.vm.exceptions import InvalidParameter
8+
from ethereum.utils.numeric import divmod
9+
from ethereum.prague.vm.gas import charge_gas
10+
from ethereum_types.numeric import Uint
11+
from ethereum_types.bytes import Bytes
12+
from cairo_core.comparison import is_zero, is_not_zero
13+
14+
// @notice The bls12_381 pairing precompile.
15+
// @dev The implementation is unsound.
16+
func bls12_pairing{
17+
range_check_ptr,
18+
bitwise_ptr: BitwiseBuiltin*,
19+
keccak_ptr: felt*,
20+
poseidon_ptr: PoseidonBuiltin*,
21+
range_check96_ptr: felt*,
22+
add_mod_ptr: ModBuiltin*,
23+
mul_mod_ptr: ModBuiltin*,
24+
evm: Evm,
25+
}() -> EthereumException* {
26+
alloc_locals;
27+
let data = evm.value.message.value.data;
28+
let (q, r) = divmod(data.value.len, 384);
29+
let data_multiple_of_384 = is_not_zero(r);
30+
let data_is_zero = is_zero(data.value.len);
31+
let invalid_valid_input = data_multiple_of_384 + data_is_zero;
32+
33+
if (invalid_valid_input != 0) {
34+
tempvar err = new EthereumException(InvalidParameter);
35+
return err;
36+
}
37+
38+
// GAS
39+
let gas_cost = Uint(32600 * q + 37700);
40+
let err = charge_gas(gas_cost);
41+
if (cast(err, felt) != 0) {
42+
return err;
43+
}
44+
45+
// OPERATION
46+
tempvar data = evm.value.message.value.data;
47+
tempvar error: EthereumException*;
48+
tempvar output: Bytes;
49+
50+
%{ bls12_pairing_hint %}
51+
if (cast(error, felt) != 0) {
52+
return error;
53+
}
54+
55+
EvmImpl.set_output(output);
56+
tempvar ok = cast(0, EthereumException*);
57+
return ok;
58+
}

cairo/ethereum/prague/vm/precompiled_contracts/mapping.cairo

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,20 @@ from ethereum.prague.vm.precompiled_contracts.ecrecover import ecrecover
1414
from ethereum.prague.vm.precompiled_contracts.blake2f import blake2f
1515
from ethereum.prague.vm.precompiled_contracts.point_evaluation import point_evaluation
1616
from ethereum.prague.vm.precompiled_contracts.ripemd160 import ripemd160
17+
from ethereum.prague.vm.precompiled_contracts.bls12_381.bls12_381_g1 import (
18+
bls12_g1_add,
19+
bls12_g1_msm,
20+
bls12_map_fp_to_g1,
21+
)
22+
from ethereum.prague.vm.precompiled_contracts.bls12_381.bls12_381_g2 import (
23+
bls12_g2_add,
24+
bls12_g2_msm,
25+
bls12_map_fp2_to_g2,
26+
)
27+
from ethereum.prague.vm.precompiled_contracts.bls12_381.bls12_381_pairing import bls12_pairing
1728
from cairo_core.control_flow import raise
18-
// currently 10 precompiles.
19-
const N_PRECOMPILES = 10;
29+
// currently 17 precompiles.
30+
const N_PRECOMPILES = 17;
2031
const HIGHEST_PRECOMPILE_LEADING_BYTE = 0x0a;
2132

2233
// count 3 steps per index: precompile_address, call, precompile_fn
@@ -104,6 +115,20 @@ func precompile_table_lookup{range_check_ptr}(address: felt) -> (felt, felt) {
104115
call blake2f; // BLAKE2F
105116
dw 0xa00000000000000000000000000000000000000;
106117
call point_evaluation; // POINT_EVALUATION
118+
dw 0xb00000000000000000000000000000000000000;
119+
call bls12_g1_add; // BLS12_G1ADD
120+
dw 0xc00000000000000000000000000000000000000;
121+
call bls12_g1_msm; // BLS12_G1MSM
122+
dw 0xd00000000000000000000000000000000000000;
123+
call bls12_g2_add; // BLS12_G2ADD
124+
dw 0xe00000000000000000000000000000000000000;
125+
call bls12_g2_msm; // BLS12_G2MSM
126+
dw 0xf00000000000000000000000000000000000000;
127+
call bls12_pairing; // BLS12_PAIRING_CHECK
128+
dw 0x100000000000000000000000000000000000000;
129+
call bls12_map_fp_to_g1; // BLS12_MAP_FP_TO_G1
130+
dw 0x110000000000000000000000000000000000000;
131+
call bls12_map_fp2_to_g2; // BLS12_MAP_FP2_TO_G2
107132
// not reached.
108133
ret;
109134
}

0 commit comments

Comments
 (0)