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{
|
var PrecompiledContractsMina = map[common.Address]PrecompiledContract{
|
||||||
common.BytesToAddress([]byte{0x50}): &MinaPoseidon{},
|
common.BytesToAddress([]byte{0x50}): &MinaPoseidon{},
|
||||||
|
common.BytesToAddress([]byte{0x51}): &MinaSigner{},
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,16 @@ package vm
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"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{}
|
type MinaPoseidon struct{}
|
||||||
|
|
||||||
func (c *MinaPoseidon) RequiredGas(input []byte) uint64 {
|
func (c *MinaPoseidon) RequiredGas(input []byte) uint64 {
|
||||||
|
|
@ -19,32 +24,37 @@ func (c *MinaPoseidon) RequiredGas(input []byte) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x1f831f84
|
// 0x1f831f84
|
||||||
var minaPoseidonSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4]
|
var poseidonHashSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4]
|
||||||
|
|
||||||
const networkIdIndex = 0
|
|
||||||
const fieldsHeadIndex = networkIdIndex + 32
|
|
||||||
|
|
||||||
func (c *MinaPoseidon) Run(input []byte) ([]byte, error) {
|
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
|
return nil, ErrExecutionReverted
|
||||||
}
|
}
|
||||||
|
|
||||||
calldata := input[4:]
|
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()
|
if err != nil {
|
||||||
fieldsLen := new(big.Int).SetBytes(getData(calldata, lenIndex, 32)).Uint64()
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
dataIndex := lenIndex + 32
|
networkId := unpacked[0].(uint8)
|
||||||
fields := calldata[dataIndex : dataIndex+fieldsLen*32]
|
fields := unpacked[1].([][32]uint8)
|
||||||
|
|
||||||
|
if len(fields) == 0 {
|
||||||
|
return nil, ErrExecutionReverted
|
||||||
|
}
|
||||||
|
|
||||||
output_buffer := [32]byte{}
|
output_buffer := [32]byte{}
|
||||||
|
|
||||||
if !C.poseidon_hash(
|
if !C.poseidon_hash(
|
||||||
uint32(networkId),
|
C.uint8_t(networkId),
|
||||||
(*C.uint8_t)(&fields[0]),
|
(*C.uint8_t)(&fields[0][0]),
|
||||||
C.uintptr_t(fieldsLen),
|
C.uintptr_t(len(fields)),
|
||||||
(*C.uint8_t)(&output_buffer[0]),
|
(*C.uint8_t)(&output_buffer[0]),
|
||||||
) {
|
) {
|
||||||
return nil, ErrExecutionReverted
|
return nil, ErrExecutionReverted
|
||||||
|
|
@ -52,3 +62,61 @@ func (c *MinaPoseidon) Run(input []byte) ([]byte, error) {
|
||||||
|
|
||||||
return output_buffer[:], nil
|
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 std::array::TryFromSliceError;
|
||||||
|
|
||||||
use mina::{Message, NetworkId};
|
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;
|
use o1_utils::FieldHelpers;
|
||||||
|
|
||||||
pub const FIELD_SIZE: usize = 32;
|
pub const FIELD_SIZE: usize = 32;
|
||||||
|
|
@ -16,9 +17,9 @@ pub extern "C" fn poseidon_hash(
|
||||||
output_ptr: *mut u8, // 32 bytes
|
output_ptr: *mut u8, // 32 bytes
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let network_id = match network_id {
|
let network_id = match network_id {
|
||||||
0x00 => NetworkId::TESTNET,
|
0x00 => NetworkId::MAINNET,
|
||||||
0x01 => NetworkId::MAINNET,
|
0x01 => NetworkId::TESTNET,
|
||||||
0xff => NetworkId::NULLNET,
|
0x02 => NetworkId::NULLNET,
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -47,6 +48,66 @@ pub extern "C" fn poseidon_hash(
|
||||||
true
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
// use super::*;
|
// use super::*;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use mina_hasher::{DomainParameter, Hashable, Hasher, ROInput};
|
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};
|
use o1_utils::{field_helpers::FieldHelpersError, FieldHelpers};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum NetworkId {
|
pub enum NetworkId {
|
||||||
TESTNET = 0x00,
|
MAINNET = 0x00,
|
||||||
MAINNET = 0x01,
|
TESTNET = 0x01,
|
||||||
NULLNET = 0xff,
|
NULLNET = 0x02,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NetworkId> for u8 {
|
impl From<NetworkId> for u8 {
|
||||||
|
|
@ -24,7 +24,7 @@ impl DomainParameter for NetworkId {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
fields: Vec<BaseField>,
|
pub fields: Vec<BaseField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
|
|
@ -61,3 +61,14 @@ pub fn poseidon(msg: &Message, network_id: NetworkId) -> BaseField {
|
||||||
|
|
||||||
hasher.hash(msg)
|
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