Add mina verify precompile

This commit is contained in:
Martin Ondejka 2023-04-05 23:19:59 +02:00
parent 7c5c706016
commit 252a962d6a
4 changed files with 164 additions and 23 deletions

View file

@ -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 (

View file

@ -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)
}

View file

@ -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::*;

View file

@ -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)
}