mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
Add mina verify precompile
This commit is contained in:
parent
7c5c706016
commit
252a962d6a
4 changed files with 164 additions and 23 deletions
|
|
@ -106,6 +106,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
|
|||
|
||||
var PrecompiledContractsMina = map[common.Address]PrecompiledContract{
|
||||
common.BytesToAddress([]byte{0x50}): &MinaPoseidon{},
|
||||
common.BytesToAddress([]byte{0x51}): &MinaSigner{},
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -7,11 +7,16 @@ package vm
|
|||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
var sol_bool, _ = abi.NewType("bool", "", nil)
|
||||
var sol_uint8, _ = abi.NewType("uint8", "", nil)
|
||||
var sol_bytes32, _ = abi.NewType("bytes32", "", nil)
|
||||
var sol_bytes32Arr, _ = abi.NewType("bytes32[]", "", nil)
|
||||
|
||||
type MinaPoseidon struct{}
|
||||
|
||||
func (c *MinaPoseidon) RequiredGas(input []byte) uint64 {
|
||||
|
|
@ -19,32 +24,37 @@ func (c *MinaPoseidon) RequiredGas(input []byte) uint64 {
|
|||
}
|
||||
|
||||
// 0x1f831f84
|
||||
var minaPoseidonSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4]
|
||||
|
||||
const networkIdIndex = 0
|
||||
const fieldsHeadIndex = networkIdIndex + 32
|
||||
var poseidonHashSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4]
|
||||
|
||||
func (c *MinaPoseidon) Run(input []byte) ([]byte, error) {
|
||||
if !bytes.Equal(input[:4], minaPoseidonSignature) {
|
||||
if len(input) < 4+64 || !bytes.Equal(input[:4], poseidonHashSignature) {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
|
||||
calldata := input[4:]
|
||||
|
||||
networkId := new(big.Int).SetBytes(getData(calldata, networkIdIndex, 32)).Uint64()
|
||||
unpacked, err := (abi.Arguments{{
|
||||
Type: sol_uint8}, // networkId
|
||||
{Type: sol_bytes32Arr}, // fields
|
||||
}).Unpack(calldata)
|
||||
|
||||
lenIndex := new(big.Int).SetBytes(getData(calldata, fieldsHeadIndex, 32)).Uint64()
|
||||
fieldsLen := new(big.Int).SetBytes(getData(calldata, lenIndex, 32)).Uint64()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dataIndex := lenIndex + 32
|
||||
fields := calldata[dataIndex : dataIndex+fieldsLen*32]
|
||||
networkId := unpacked[0].(uint8)
|
||||
fields := unpacked[1].([][32]uint8)
|
||||
|
||||
if len(fields) == 0 {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
|
||||
output_buffer := [32]byte{}
|
||||
|
||||
if !C.poseidon_hash(
|
||||
uint32(networkId),
|
||||
(*C.uint8_t)(&fields[0]),
|
||||
C.uintptr_t(fieldsLen),
|
||||
C.uint8_t(networkId),
|
||||
(*C.uint8_t)(&fields[0][0]),
|
||||
C.uintptr_t(len(fields)),
|
||||
(*C.uint8_t)(&output_buffer[0]),
|
||||
) {
|
||||
return nil, ErrExecutionReverted
|
||||
|
|
@ -52,3 +62,61 @@ func (c *MinaPoseidon) Run(input []byte) ([]byte, error) {
|
|||
|
||||
return output_buffer[:], nil
|
||||
}
|
||||
|
||||
type MinaSigner struct{}
|
||||
|
||||
func (c *MinaSigner) RequiredGas(input []byte) uint64 {
|
||||
return 1000
|
||||
}
|
||||
|
||||
// 0x462e39d6
|
||||
var verifySignature = crypto.Keccak256([]byte("verify(uint8,bytes32,bytes32,bytes32,bytes32,bytes32[])"))[:4]
|
||||
|
||||
func (c *MinaSigner) Run(input []byte) ([]byte, error) {
|
||||
if len(input) < 4+64 || !bytes.Equal(input[:4], verifySignature) {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
|
||||
calldata := input[4:]
|
||||
|
||||
unpacked, err := (abi.Arguments{
|
||||
{Type: sol_uint8}, // networkId
|
||||
{Type: sol_bytes32}, // pubKeyX
|
||||
{Type: sol_bytes32}, // pubKeyY
|
||||
{Type: sol_bytes32}, // signatureRX
|
||||
{Type: sol_bytes32}, // signatureS
|
||||
{Type: sol_bytes32Arr}, // fields
|
||||
}).Unpack(calldata)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
networkId := unpacked[0].(uint8)
|
||||
pubKeyX := unpacked[1].([32]uint8)
|
||||
pubKeyY := unpacked[2].([32]uint8)
|
||||
signatureRX := unpacked[3].([32]uint8)
|
||||
signatureS := unpacked[4].([32]uint8)
|
||||
fields := unpacked[5].([][32]uint8)
|
||||
|
||||
if len(fields) == 0 {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
|
||||
output_buffer := false
|
||||
|
||||
if !C.verify(
|
||||
C.uint8_t(networkId),
|
||||
(*C.uint8_t)(&pubKeyX[0]),
|
||||
(*C.uint8_t)(&pubKeyY[0]),
|
||||
(*C.uint8_t)(&signatureRX[0]),
|
||||
(*C.uint8_t)(&signatureS[0]),
|
||||
(*C.uint8_t)(&fields[0][0]),
|
||||
C.uintptr_t(len(fields)),
|
||||
(*C.bool)(&output_buffer),
|
||||
) {
|
||||
return nil, ErrExecutionReverted
|
||||
}
|
||||
|
||||
return abi.Arguments{{Type: sol_bool}}.Pack(output_buffer)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ mod mina;
|
|||
use std::array::TryFromSliceError;
|
||||
|
||||
use mina::{Message, NetworkId};
|
||||
use mina_signer::{BaseField, CurvePoint, PubKey, Signature, ScalarField};
|
||||
use mina_hasher::Hashable;
|
||||
use mina_signer::{BaseField, CurvePoint, PubKey, ScalarField, Signature};
|
||||
use o1_utils::FieldHelpers;
|
||||
|
||||
pub const FIELD_SIZE: usize = 32;
|
||||
|
|
@ -16,9 +17,9 @@ pub extern "C" fn poseidon_hash(
|
|||
output_ptr: *mut u8, // 32 bytes
|
||||
) -> bool {
|
||||
let network_id = match network_id {
|
||||
0x00 => NetworkId::TESTNET,
|
||||
0x01 => NetworkId::MAINNET,
|
||||
0xff => NetworkId::NULLNET,
|
||||
0x00 => NetworkId::MAINNET,
|
||||
0x01 => NetworkId::TESTNET,
|
||||
0x02 => NetworkId::NULLNET,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
|
|
@ -47,6 +48,66 @@ pub extern "C" fn poseidon_hash(
|
|||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn verify(
|
||||
network_id: u8,
|
||||
pubkey_x: *const u8,
|
||||
pubkey_y: *const u8,
|
||||
sig_rx: *const u8,
|
||||
sig_s: *const u8,
|
||||
field_ptr: *const u8,
|
||||
field_len: usize,
|
||||
output_ptr: *mut bool,
|
||||
) -> bool {
|
||||
let network_id = match network_id {
|
||||
0x00 => NetworkId::MAINNET,
|
||||
0x01 => NetworkId::TESTNET,
|
||||
0x02 => NetworkId::NULLNET,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let pubkey_x = unsafe { std::slice::from_raw_parts(pubkey_x, FIELD_SIZE) };
|
||||
let pubkey_y = unsafe { std::slice::from_raw_parts(pubkey_y, FIELD_SIZE) };
|
||||
|
||||
let pubkey = PubKey::from_point_unsafe(CurvePoint::new(
|
||||
BaseField::from_bytes(pubkey_x).unwrap(),
|
||||
BaseField::from_bytes(pubkey_y).unwrap(),
|
||||
false,
|
||||
));
|
||||
|
||||
let sig_rx = unsafe { std::slice::from_raw_parts(sig_rx, FIELD_SIZE) };
|
||||
let sig_s = unsafe { std::slice::from_raw_parts(sig_s, FIELD_SIZE) };
|
||||
|
||||
let signature = Signature::new(
|
||||
BaseField::from_bytes(sig_rx).unwrap(),
|
||||
ScalarField::from_bytes(sig_s).unwrap(),
|
||||
);
|
||||
|
||||
let fields = unsafe { std::slice::from_raw_parts(field_ptr, field_len * FIELD_SIZE) };
|
||||
|
||||
let fields = match fields
|
||||
.chunks(FIELD_SIZE)
|
||||
.map(|chunk| chunk[..32].try_into())
|
||||
.collect::<Result<Vec<[u8; 32]>, TryFromSliceError>>()
|
||||
{
|
||||
Ok(fields) => fields,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
let msg = match Message::from_bytes_slice(&fields) {
|
||||
Ok(msg) => msg,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
println!("{:?}", msg.to_roinput().to_fields()[0].to_biguint());
|
||||
|
||||
let result = mina::verify(&signature, &pubkey, &msg, network_id);
|
||||
|
||||
unsafe { *output_ptr = result };
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// use super::*;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use mina_hasher::{DomainParameter, Hashable, Hasher, ROInput};
|
||||
use mina_signer::{BaseField, PubKey, Signer, Signature};
|
||||
use mina_signer::{BaseField, PubKey, Signature, Signer};
|
||||
use o1_utils::{field_helpers::FieldHelpersError, FieldHelpers};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub enum NetworkId {
|
||||
TESTNET = 0x00,
|
||||
MAINNET = 0x01,
|
||||
NULLNET = 0xff,
|
||||
MAINNET = 0x00,
|
||||
TESTNET = 0x01,
|
||||
NULLNET = 0x02,
|
||||
}
|
||||
|
||||
impl From<NetworkId> for u8 {
|
||||
|
|
@ -24,7 +24,7 @@ impl DomainParameter for NetworkId {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Message {
|
||||
fields: Vec<BaseField>,
|
||||
pub fields: Vec<BaseField>,
|
||||
}
|
||||
|
||||
impl Message {
|
||||
|
|
@ -61,3 +61,14 @@ pub fn poseidon(msg: &Message, network_id: NetworkId) -> BaseField {
|
|||
|
||||
hasher.hash(msg)
|
||||
}
|
||||
|
||||
pub fn verify(
|
||||
signature: &Signature,
|
||||
pubkey: &PubKey,
|
||||
msg: &Message,
|
||||
network_id: NetworkId,
|
||||
) -> bool {
|
||||
let mut signer = mina_signer::create_kimchi::<Message>(network_id);
|
||||
|
||||
signer.verify(signature, pubkey, msg)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue