diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 1b832b6386..20c5d1c96b 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -104,11 +104,16 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{18}): &bls12381MapG2{}, } +var PrecompiledContractsMina = map[common.Address]PrecompiledContract{ + common.BytesToAddress([]byte{0x50}): &MinaPoseidon{}, +} + var ( PrecompiledAddressesBerlin []common.Address PrecompiledAddressesIstanbul []common.Address PrecompiledAddressesByzantium []common.Address PrecompiledAddressesHomestead []common.Address + PrecompiledAddressesMina []common.Address ) func init() { @@ -124,19 +129,24 @@ func init() { for k := range PrecompiledContractsBerlin { PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k) } + for k := range PrecompiledContractsMina { + PrecompiledAddressesMina = append(PrecompiledAddressesMina, k) + } } // ActivePrecompiles returns the precompiles enabled with the current configuration. func ActivePrecompiles(rules params.Rules) []common.Address { + result := PrecompiledAddressesMina + switch { case rules.IsBerlin: - return PrecompiledAddressesBerlin + return append(result, PrecompiledAddressesBerlin...) case rules.IsIstanbul: - return PrecompiledAddressesIstanbul + return append(result, PrecompiledAddressesIstanbul...) case rules.IsByzantium: - return PrecompiledAddressesByzantium + return append(result, PrecompiledAddressesByzantium...) default: - return PrecompiledAddressesHomestead + return append(result, PrecompiledAddressesHomestead...) } } diff --git a/core/vm/evm.go b/core/vm/evm.go index dd55618bf8..dd873c4674 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -42,6 +42,10 @@ type ( ) func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { + if p, ok := PrecompiledContractsMina[addr]; ok { + return p, ok + } + var precompiles map[common.Address]PrecompiledContract switch { case evm.chainRules.IsBerlin: @@ -53,6 +57,7 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { default: precompiles = PrecompiledContractsHomestead } + p, ok := precompiles[addr] return p, ok } diff --git a/core/vm/mina_contracts.go b/core/vm/mina_contracts.go new file mode 100644 index 0000000000..7acdf6b4bf --- /dev/null +++ b/core/vm/mina_contracts.go @@ -0,0 +1,54 @@ +package vm + +/* +#cgo LDFLAGS: mina/target/release/libmina.a -ldl +#include "../../mina/target/mina.h" +*/ +import "C" +import ( + "bytes" + "math/big" + + "github.com/ethereum/go-ethereum/crypto" +) + +type MinaPoseidon struct{} + +func (c *MinaPoseidon) RequiredGas(input []byte) uint64 { + return 1000 +} + +// 0x1f831f84 +var minaPoseidonSignature = crypto.Keccak256([]byte("poseidonHash(uint8,bytes32[])"))[:4] + +const networkIdIndex = 0 +const fieldsHeadIndex = networkIdIndex + 32 + +func (c *MinaPoseidon) Run(input []byte) ([]byte, error) { + if !bytes.Equal(input[:4], minaPoseidonSignature) { + return nil, ErrExecutionReverted + } + + calldata := input[4:] + + networkId := new(big.Int).SetBytes(getData(calldata, networkIdIndex, 32)).Uint64() + + lenIndex := new(big.Int).SetBytes(getData(calldata, fieldsHeadIndex, 32)).Uint64() + fieldsLen := new(big.Int).SetBytes(getData(calldata, lenIndex, 32)).Uint64() + + dataIndex := lenIndex + 32 + fields := calldata[dataIndex : dataIndex+fieldsLen*32] + + output_buffer := [32]byte{} + + if !C.poseidon_hash( + uint32(networkId), + (*C.uint8_t)(&fields[0]), + C.uintptr_t(fieldsLen), + (*C.uint8_t)(&output_buffer[0]), + ) { + return nil, ErrExecutionReverted + } + + return output_buffer[:], nil +} diff --git a/mina/.gitignore b/mina/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/mina/.gitignore @@ -0,0 +1 @@ +/target