Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

Commit

Permalink
Added the 6th,7th and 8th precompile, its tests and a zk-snark test f…
Browse files Browse the repository at this point in the history
…or test them all together

Signed-off-by: matias diaz <[email protected]>
  • Loading branch information
matiasADiazPerez committed Oct 10, 2019
1 parent 9aeb68c commit 9e7b87e
Show file tree
Hide file tree
Showing 8 changed files with 938 additions and 8 deletions.
43 changes: 43 additions & 0 deletions execution/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package evm
import (
"bytes"
"encoding/binary"
"fmt"
"math/big"
"reflect"
"testing"
Expand Down Expand Up @@ -1443,6 +1444,48 @@ func TestEVM(t *testing.T) {
}
}
})

t.Run("SnarkProof", func(t *testing.T) {
st := acmstate.NewMemoryState()
blockchain := new(blockchain)
eventSink := exec.NewNoopEventSink()
txe := new(exec.TxExecution)
success := "0000000000000000000000000000000000000000000000000000000000000001"

account1 := newAccount(t, st, "1")

var gas uint64 = 1000000
/*
contract snark_proof.sol on execution/solidity
*/

// This bytecode is compiled from Solidity contract above using remix.ethereum.org online compiler
code, err := hex.DecodeString("608060405234801561001057600080fd5b5061109c806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063dd12931314610030575b600080fd5b61018f600480360361012081101561004757600080fd5b8101908080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f82011690508083019250505050505091929192908060800190600280602002604051908101604052809291906000905b828210156100fc578382604002016002806020026040519081016040528092919082600260200280828437600081840152601f19601f820116905080830192505050505050815260200190600101906100a8565b50505050919291929080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f820116905080830192505050505050919291929080602001906001806020026040519081016040528092919082600160200280828437600081840152601f19601f82011690508083019250505050505091929192905050506101b9565b604051808267ffffffffffffffff1667ffffffffffffffff16815260200191505060405180910390f35b60006101c3610ee9565b6040518060400160405280876000600281106101db57fe5b60200201518152602001876001600281106101f257fe5b60200201518152508160000181905250604051806040016040528060405180604001604052808860006002811061022557fe5b602002015160006002811061023657fe5b602002015181526020018860006002811061024d57fe5b602002015160016002811061025e57fe5b6020020151815250815260200160405180604001604052808860016002811061028357fe5b602002015160006002811061029457fe5b60200201518152602001886001600281106102ab57fe5b60200201516001600281106102bc57fe5b602002015181525081525081602001819052506040518060400160405280856000600281106102e757fe5b60200201518152602001856001600281106102fe57fe5b60200201518152508160400181905250606060016040519080825280602002602001820160405280156103405781602001602082028038833980820191505090505b50905060008090505b60018110156103885784816001811061035e57fe5b602002015182828151811061036f57fe5b6020026020010181815250508080600101915050610349565b5060006103958284610400565b14156103f1577f3f3cfdb26fb5f9f1786ab4f1a1f9cd4c0b5e726cbdfc26e495261731aad44e396040518080602001828103825260228152602001806110466022913960400191505060405180910390a16001925050506103f8565b6000925050505b949350505050565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905061042e610f1c565b610436610572565b905080608001515160018651011461044d57600080fd5b610455610f63565b6040518060400160405280600081526020016000815250905060008090505b86518110156104eb578387828151811061048a57fe5b60200260200101511061049c57600080fd5b6104dc826104d7856080015160018501815181106104b657fe5b60200260200101518a85815181106104ca57fe5b602002602001015161094f565b6109e3565b91508080600101915050610474565b5061050e81836080015160008151811061050157fe5b60200260200101516109e3565b90506105548560000151866020015161052684610a96565b85604001516105388a60400151610a96565b876060015161054a8960000151610a96565b8960200151610b30565b610564576001935050505061056c565b600093505050505b92915050565b61057a610f1c565b60405180604001604052807f0e418100f073ea28e62635436dfef6662084b9806ce0ad27b3613c937547afaf81526020017f2715cf4a1ab651aa12224d4d89ff7b6c7d52de41d685a8e3f858dbf7cf4bb86c8152508160000181905250604051806040016040528060405180604001604052807f2c45036fa487081f7dceff7fc25f21ecb503280d6709afeb2ef267062a17bfaa81526020017f2bfdb5e21864c4dd66403e22e27b177159a41d8d6083d242316f279cb402a6af815250815260200160405180604001604052807f19130dbec8ab7a1e680dfb30b1095362ac90bda4cc286b02f6dcf066814374ba81526020017f0fb951bad964841c50f4959173906e4ab9dcdb1e3d8ce23a608ce62dffc627b98152508152508160200181905250604051806040016040528060405180604001604052807f1b406529320c585cd88fc96b447bd4d2b2628684924d3308fe5a3da3fa7ef3b281526020017f2c610b42fc4ef3486859cf17c701911614d1ea8835766cc1c7b6b10105b9a91a815250815260200160405180604001604052807f1aebcf6a745eb8e2619df8459519a62585a525e9eb2e0d21a7860ab295dc83af81526020017f169479ce2e165fa60f74d9b2c9dbef898693bbd057d65a3dc71393bb267f4f538152508152508160400181905250604051806040016040528060405180604001604052807f117693772d6758fa5c40fb20e7da8014a16787a51dae05c9ce84eeaecc562d4781526020017f28cde745918425d0ff0aee33d9d9c40b0c55e17b3eb8f9349a535b854b6986e7815250815260200160405180604001604052807f1d3789e7c9fe3aa18efd584b0788dd6a6c9e096e6feefb17a3074ccb4603693581526020017f29ca1920396f97ba9604a61627e7c871d7784b8cfb05548e861d08cb3faa3c5d8152508152508160600181905250600260405190808252806020026020018201604052801561086157816020015b61084e610f7d565b8152602001906001900390816108465790505b50816080018190525060405180604001604052807f15069c841ae9a81471054e59a0f8368742cdb9e868f90697e25801f6919ba09c81526020017f14f48d7ef7fc76b76e45f96041c01a6d86a4f0ae1aa3811ddf97e73a483883da81525081608001516000815181106108d057fe5b602002602001018190525060405180604001604052807f2ca7396acba421c38c761d240608f70f49c28340c9fe8ae415767f10ddbf0f5d81526020017f09d0792a0b5f5242c6b39dd102d3a4fd5858cd8c93ce2d337f310a5c23f28862815250816080015160018151811061094157fe5b602002602001018190525090565b610957610f63565b61095f610f97565b83600001518160006003811061097157fe5b60200201818152505083602001518160016003811061098c57fe5b60200201818152505082816002600381106109a357fe5b6020020181815250506000606083608084600060076107d05a03f1905080600081146109ce576109d0565bfe5b50806109db57600080fd5b505092915050565b6109eb610f63565b6109f3610fb9565b836000015181600060048110610a0557fe5b602002018181525050836020015181600160048110610a2057fe5b602002018181525050826000015181600260048110610a3b57fe5b602002018181525050826020015181600360048110610a5657fe5b602002018181525050600060608360c084600060066107d05a03f190508060008114610a8157610a83565bfe5b5080610a8e57600080fd5b505092915050565b610a9e610f63565b60007f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47905060008360000151148015610adb575060008360200151145b15610aff576040518060400160405280600081526020016000815250915050610b2b565b60405180604001604052808460000151815260200182856020015181610b2157fe5b0683038152509150505b919050565b600060606004604051908082528060200260200182016040528015610b6f57816020015b610b5c610f7d565b815260200190600190039081610b545790505b50905060606004604051908082528060200260200182016040528015610baf57816020015b610b9c610fdb565b815260200190600190039081610b945790505b5090508a82600081518110610bc057fe5b60200260200101819052508882600181518110610bd957fe5b60200260200101819052508682600281518110610bf257fe5b60200260200101819052508482600381518110610c0b57fe5b60200260200101819052508981600081518110610c2457fe5b60200260200101819052508781600181518110610c3d57fe5b60200260200101819052508581600281518110610c5657fe5b60200260200101819052508381600381518110610c6f57fe5b6020026020010181905250610c848282610c94565b9250505098975050505050505050565b60008151835114610ca457600080fd5b6000835190506000600682029050606081604051908082528060200260200182016040528015610ce35781602001602082028038833980820191505090505b50905060008090505b83811015610e8957868181518110610d0057fe5b602002602001015160000151826000600684020181518110610d1e57fe5b602002602001018181525050868181518110610d3657fe5b602002602001015160200151826001600684020181518110610d5457fe5b602002602001018181525050858181518110610d6c57fe5b602002602001015160000151600060028110610d8457fe5b6020020151826002600684020181518110610d9b57fe5b602002602001018181525050858181518110610db357fe5b602002602001015160000151600160028110610dcb57fe5b6020020151826003600684020181518110610de257fe5b602002602001018181525050858181518110610dfa57fe5b602002602001015160200151600060028110610e1257fe5b6020020151826004600684020181518110610e2957fe5b602002602001018181525050858181518110610e4157fe5b602002602001015160200151600160028110610e5957fe5b6020020151826005600684020181518110610e7057fe5b6020026020010181815250508080600101915050610cec565b50610e92611001565b60006020826020860260208601600060086107d05a03f190508060008114610eb957610ebb565bfe5b5080610ec657600080fd5b600082600060018110610ed557fe5b602002015114159550505050505092915050565b6040518060600160405280610efc610f7d565b8152602001610f09610fdb565b8152602001610f16610f7d565b81525090565b6040518060a00160405280610f2f610f7d565b8152602001610f3c610fdb565b8152602001610f49610fdb565b8152602001610f56610fdb565b8152602001606081525090565b604051806040016040528060008152602001600081525090565b604051806040016040528060008152602001600081525090565b6040518060600160405280600390602082028038833980820191505090505090565b6040518060800160405280600490602082028038833980820191505090505090565b6040518060400160405280610fee611023565b8152602001610ffb611023565b81525090565b6040518060200160405280600190602082028038833980820191505090505090565b604051806040016040528060029060208202803883398082019150509050509056fe5472616e73616374696f6e207375636365737366756c6c792076657269666965642ea265627a7a72315820370d27c03786385e17679581219c564d5cab475959d2258e687395be7135499764736f6c634300050b0032")
require.NoError(t, err)

params := engine.CallParams{
Caller: account1,
Input: code,
Gas: &gas,
}

vm := New(Options{})
// Run the contract
contractCode, err := vm.Execute(st, blockchain, eventSink, params, code)
require.NoError(t, err)

err = native.InitCode(st, account1, contractCode)
require.NoError(t, err)
// proof of the knowledge of 2 int x,y that satisfies x+y == 7, you never will know which ones ;)
params.Input, err = hex.DecodeString("dd1293131f2ccd45a0b09042eb57d4450d7a7c29756a6fa15d90b4be6a1b6137dfa74b3d04fe23902ce8eafcfd08575d543cd6d90a8fb4cee612cb7595fca4f27e1e59a821404670659770738d45c4e0ebde16e3a92cde672a183910b2842f04437d059c0aac54431da98c9a4b5ed1f0748f09a2786410cb72ec504de60c8348e950c7630cb1c9d20bc1e2d4a50b732c3254f10b1c103e242c71bd4ee957de8233f31f8818e4e54e2440cf16f062e358737f97a69850edab4dcf6fcc0624718b2b1c2f2425b8c5ecf1280d9e8643a1ea8cb31a174816dfd070d4b9498f51467705d0963c0e145c31d12be80bc23ab2431ed4d307c7d1bc9df33090c2c766cdb15ca539580000000000000000000000000000000000000000000000000000000000000001")
require.NoError(t, err)

out, errx := vm.Execute(st, blockchain, txe, params, contractCode)
// out must be solidity uint64(1)
assert.NoError(t, errx)
assert.Equal(t, success, fmt.Sprintf("%x", out))

})
}

type blockchain struct {
Expand Down
4 changes: 4 additions & 0 deletions execution/native/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ const (
GasExpModBase uint64 = 1
GasIdentityWord uint64 = 1
GasIdentityBase uint64 = 1

GasBn256Add uint64 = 1
GasBn256ScalarMul uint64 = 1
GasBn256Pairing uint64 = 1
)
144 changes: 136 additions & 8 deletions execution/native/precompiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math/big"

"github.com/clearmatics/bn256"
"github.com/hyperledger/burrow/binary"
"github.com/hyperledger/burrow/crypto"
"github.com/hyperledger/burrow/execution/errors"
Expand All @@ -28,7 +29,19 @@ var Precompiles = New().
MustFunction(`Compute the operation base**exp % mod where the values are big ints`,
leftPadAddress(5),
permission.None,
expModFunc)
expModFunc).
MustFunction(`Return the add of two points on a bn256 curve`,
leftPadAddress(6),
permission.None,
bn256Add).
MustFunction(`Return the scalar multiplication of a big int and a point on a bn256 curve`,
leftPadAddress(7),
permission.None,
bn256ScalarMul).
MustFunction(`Check the pairing of a set of points on a bn256 curve `,
leftPadAddress(8),
permission.None,
bn256Pairing)

func leftPadAddress(bs ...byte) crypto.Address {
return crypto.AddressFromWord256(binary.LeftPadWord256(bs))
Expand Down Expand Up @@ -131,9 +144,10 @@ func expModFunc(ctx Context) (output []byte, err error) {
}

// get the values of base, exp and mod
base := getBigInt(segments[0], baseLength)
exp := getBigInt(segments[1], expLength)
mod := getBigInt(segments[2], modLength)

base := new(big.Int).SetBytes(segments[0])
exp := new(big.Int).SetBytes(segments[1])
mod := new(big.Int).SetBytes(segments[2])

// handle mod 0
if mod.Sign() == 0 {
Expand All @@ -144,6 +158,99 @@ func expModFunc(ctx Context) (output []byte, err error) {
return binary.LeftPadBytes(new(big.Int).Exp(base, exp, mod).Bytes(), int(modLength)), nil
}

// bn256Add implements the EIP-196 for add pairs in a bn256 curve https://github.com/ethereum/EIPs/blob/master/EIPS/eip-196.md
func bn256Add(ctx Context) (output []byte, err error) {

if *ctx.Gas < GasBn256Add {
return nil, errors.Codes.InsufficientGas
}
*ctx.Gas -= GasBn256Add
// retrieve the points from the input
x := new(bn256.G1)
y := new(bn256.G1)

_, sgmnt, errs := cut(ctx.Input, binary.Word256Bytes*2, binary.Word256Bytes*2)
if errs != nil {
return nil, errs
}

_, errx := x.Unmarshal(sgmnt[0])
if errx != nil {
return nil, fmt.Errorf("error x: " + errx.Error())
}

_, erry := y.Unmarshal(sgmnt[1])
if erry != nil {
return nil, fmt.Errorf("error y: " + erry.Error())
}
//add them
res := new(bn256.G1)
res.Add(x, y)
return res.Marshal(), nil
}

//bn256bn256ScalarMul implements the EIP-196 for scalar multiplication in a bn256 curve https://github.com/ethereum/EIPs/blob/master/EIPS/eip-196.md
func bn256ScalarMul(ctx Context) ([]byte, error) {
if *ctx.Gas < GasBn256ScalarMul {
return nil, errors.Codes.InsufficientGas
}
*ctx.Gas -= GasBn256ScalarMul

//retrieve the point from the input
_, sgmnt, errs := cut(ctx.Input, binary.Word256Bytes*2, binary.Word256Bytes)

if errs != nil {
return nil, errs
}

bnp := new(bn256.G1)
_, errp := bnp.Unmarshal(sgmnt[0])
if errp != nil {
return nil, errp
}
//make the scalar multiplication
res := new(bn256.G1)
res.ScalarMult(bnp, new(big.Int).SetBytes(sgmnt[1]))
return res.Marshal(), nil
}

// bn256Pairing implements the EIP-197 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md
func bn256Pairing(ctx Context) ([]byte, error) {
if *ctx.Gas < GasBn256Pairing {
return nil, errors.Codes.InsufficientGas
}

*ctx.Gas -= GasBn256Pairing

// Handle some corner cases cheaply

if len(ctx.Input)%192 > 0 {

return nil, fmt.Errorf("bad elliptic curve pairing size")
}
// auxiliars for parse the inputs
var (
cs []*bn256.G1
ts []*bn256.G2
)
// retrieving the inputs into the curve points
for i := 0; i < len(ctx.Input); i += 192 {
c, errc := newCurvePoint(ctx.Input[i : i+64])
if errc != nil {
return nil, errc
}
cs = append(cs, c)

t, errt := newTwistPoint(ctx.Input[i+64 : i+192])
if errt != nil {
return nil, errt
}
ts = append(ts, t)
}
// check the parity
return pairingCheckByte(cs, ts), nil
}

// Partition the head of input into segments for each length in lengths. The first return value is the unconsumed tail
// of input and the seconds is the segments. Returns an error if input is of insufficient length to establish each segment.
func cut(input []byte, lengths ...uint64) ([]byte, [][]byte, error) {
Expand All @@ -158,10 +265,31 @@ func cut(input []byte, lengths ...uint64) ([]byte, [][]byte, error) {
return input, segments, nil
}

func getBigInt(bs []byte, numBytes uint64) *big.Int {
bits := uint(numBytes) * 8
// Push bytes into big.Int and interpret as twos complement encoding with of bits width
return binary.FromTwosComplement(new(big.Int).SetBytes(bs), bits)
//represent bools as byte arrays of size 32. 1 for true, 0 for false
func pairingCheckByte(a []*bn256.G1, b []*bn256.G2) []byte {
if bn256.PairingCheck(a, b) {
return []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}

}
return make([]byte, binary.Word256Bytes)
}

func newCurvePoint(blob []byte) (*bn256.G1, error) {
p := new(bn256.G1)
if _, err := p.Unmarshal(blob); err != nil {
return nil, err
}
return p, nil
}

// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point,
// returning it, or an error if the point is invalid.
func newTwistPoint(blob []byte) (*bn256.G2, error) {
p := new(bn256.G2)
if _, err := p.Unmarshal(blob); err != nil {
return nil, err
}
return p, nil
}

func getUint64(bs []byte) uint64 {
Expand Down
71 changes: 71 additions & 0 deletions execution/native/precompiles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package native

import (
"encoding/hex"
"fmt"
"log"
"testing"

"github.com/stretchr/testify/require"
)

// TODO: Add more test for each precompile
const (
bigModExpInput = "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b"
bigModExpExpected = "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc"

bn256AddInput = "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
bn256AddExpected = "030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c4"

bn256ScaMulInput = "2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb721611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb20400000000000000000000000000000000000000000000000011138ce750fa15c2"
bn256ScaMulExpected = "070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc"

bn256PairingInput = "1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf704bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a416782bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"
bn256PairingExpected = "0000000000000000000000000000000000000000000000000000000000000001"
)

type precompile func(Context) ([]byte, error)

func TestBigModExp(t *testing.T) {
testPrecompile(t, expModFunc, bigModExpInput, bigModExpExpected)
}

func TestBn256Add(t *testing.T) {

testPrecompile(t, bn256Add, bn256AddInput, bn256AddExpected)

}

func TestBn256ScalarMul(t *testing.T) {

testPrecompile(t, bn256ScalarMul, bn256ScaMulInput, bn256ScaMulExpected)

}

func TestBn256Pairing(t *testing.T) {
testPrecompile(t, bn256Pairing, bn256PairingInput, bn256PairingExpected)

}

func testPrecompile(t *testing.T, pr precompile, input, success string) {
inputb, _ := hex.DecodeString(input)

ctx := setContext(inputb)
out, err := pr(*ctx)
if err != nil {
log.Println(err.Error())
t.Logf(err.Error())
t.Fail()
}
fmt.Printf("%x", out)
require.Equal(t, success, fmt.Sprintf("%x", out))

}

func setContext(input []byte) *Context {
gas := uint64(1000000)
ctx := Context{}
ctx.Input = input
ctx.Gas = &gas
return &ctx
}
Loading

0 comments on commit 9e7b87e

Please sign in to comment.