quorum/core/zether/api.go

245 lines
8.3 KiB
Go

package zether
import "C"
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/bn256/cloudflare"
"io/ioutil"
"math/big"
"math/rand"
"net/http"
"net/url"
"strings"
"time"
)
type PublicZetherAPI struct {
}
func NewPublicZetherAPI() *PublicZetherAPI {
return &PublicZetherAPI{}
}
func (api *PublicZetherAPI) TestConnect(b uint) (map[string]string, error) {
myRand := rand.New(rand.NewSource(time.Now().UnixNano()))
x, _, err := bn256.RandomG1(myRand)
// test java connectivity
req, _ := http.NewRequest("GET", "http://localhost:8080/test", nil)
q := req.URL.Query()
q.Add("a", common.BytesToHash(x.Bytes()).Hex())
q.Add("b", fmt.Sprint(b))
req.URL.RawQuery = q.Encode()
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, errors.New("Failed to execute Java request.")
}
resp_body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
return map[string]string{
"response": string(resp_body),
"status": "success",
}, nil
}
func (api *PublicZetherAPI) CreateAccount() (map[string]interface{}, error) {
result := make(map[string]interface{})
myRand := rand.New(rand.NewSource(time.Now().UnixNano())) // can i use the default source?
x, _, err := bn256.RandomG1(myRand)
y := new(bn256.G1)
gBytes, _ := hexutil.Decode("0x077da99d806abd13c9f15ece5398525119d11e11e9836b2ee7d23f6159ad87d401485efa927f2ad41bff567eec88f32fb0a0f706588b4e41a8d587d008b7f875")
y.Unmarshal(gBytes)
y.ScalarMult(y, x)
if err != nil {
return nil, err
}
result["x"] = common.BytesToHash(x.Bytes())
result["y"] = [2]common.Hash{common.BytesToHash(y.Marshal()[:32]), common.BytesToHash(y.Marshal()[32:])}
return result, nil
}
func (api *PublicZetherAPI) ReadBalance(CBytes [2][2]common.Hash, xHash common.Hash, start int64, endInt int64) (int64, error) {
// no longer checking whether start >= 0.
if endInt > big.MaxPrec {
return 0, errors.New("Invalid search range!")
}
CL := new(bn256.G1)
if _, err := CL.Unmarshal(append(CBytes[0][0].Bytes(), CBytes[0][1].Bytes()...)); err != nil {
return 0, err
}
CR := new(bn256.G1)
if _, err := CR.Unmarshal(append(CBytes[1][0].Bytes(), CBytes[1][1].Bytes()...)); err != nil {
return 0, err
}
x := new(big.Int)
xBytes, err := xHash.MarshalText()
if err != nil {
return 0, err
}
x.UnmarshalText(xBytes)
gb := new(bn256.G1)
gb.Add(CL, gb.ScalarMult(CR, x.Neg(x)))
one := big.NewInt(1)
end := big.NewInt(endInt)
gBytes, _ := hexutil.Decode("0x077da99d806abd13c9f15ece5398525119d11e11e9836b2ee7d23f6159ad87d401485efa927f2ad41bff567eec88f32fb0a0f706588b4e41a8d587d008b7f875")
for i := big.NewInt(start); i.Cmp(end) <= 0; i.Add(i, one) {
test := new(bn256.G1)
test.Unmarshal(gBytes)
test.ScalarMult(test, i)
if bytes.Compare(test.Marshal(), gb.Marshal()) == 0 {
return i.Int64(), nil
}
}
return 0, errors.New("Balance decryption failed!")
}
func mapInto(input string, i uint64) *bn256.G1 { // urgent: messed with this, not tested.
// meant to mimic the functionality of group.mapInto(ProofUtils.paddedHash(input, i))
p := hexutil.MustDecodeBig("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47") // field order
seed := new(big.Int)
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, i) // will cause failures if epoch is > 32 bits but < 64 bits.
seed.SetBytes(crypto.Keccak256([]byte(input), make([]byte, 24), buf))
seed.Mod(seed, p)
y := new(big.Int)
for {
ySquared := y.Add(y.Exp(seed, big.NewInt(3), p), big.NewInt(3)) // throw away base values
if y.ModSqrt(ySquared, p) != nil { // y.Exp(ySquared, y.Div(y.Add(p, big.NewInt(1)), big.NewInt(4)), p) // why doesn't this work?!?!?!?
break
}
seed.Add(seed, big.NewInt(1))
}
seedBytes := make([]byte, 32)
yBytes := make([]byte, 32)
copy(seedBytes[32-len(seed.Bytes()):], seed.Bytes()) // right-justify
copy(yBytes[32-len(y.Bytes()):], y.Bytes()) // right-justify
result := new(bn256.G1)
result.Unmarshal(append(seedBytes, yBytes...))
return result
}
func (api *PublicZetherAPI) ProveTransfer(CLBytes [][2]common.Hash, CRBytes [][2]common.Hash, yBytes [][2]common.Hash, epoch uint64, xBytes common.Hash, bTransfer uint64, bDiff uint64, index []uint64) (map[string]interface{}, error) {
result := make(map[string]interface{})
myRand := rand.New(rand.NewSource(time.Now().UnixNano()))
r, _, err := bn256.RandomG1(myRand)
size := len(yBytes)
var CL strings.Builder
var CR strings.Builder
var y strings.Builder
CL.WriteString("0x")
CR.WriteString("0x")
y.WriteString("0x")
for i := 0; i < size; i++ {
CL.WriteString(hexutil.Encode(append(CLBytes[i][0].Bytes(), CLBytes[i][1].Bytes()...))[2:])
CR.WriteString(hexutil.Encode(append(CRBytes[i][0].Bytes(), CRBytes[i][1].Bytes()...))[2:])
y.WriteString(hexutil.Encode(append(yBytes[i][0].Bytes(), yBytes[i][1].Bytes()...))[2:])
}
// RPC to Java service
q := url.Values{}
q.Add("CL", CL.String())
q.Add("CR", CR.String())
q.Add("y", y.String())
q.Add("epoch", hexutil.EncodeUint64(epoch))
q.Add("x", hexutil.Encode(xBytes.Bytes()))
q.Add("r", common.BytesToHash(r.Bytes()).Hex())
q.Add("bTransfer", hexutil.EncodeUint64(bTransfer))
q.Add("bDiff", hexutil.EncodeUint64(bDiff))
q.Add("outIndex", hexutil.EncodeUint64(index[0]))
q.Add("inIndex", hexutil.EncodeUint64(index[1]))
resp, err := http.PostForm("http://localhost:8080/prove-transfer", q)
if err != nil {
return nil, errors.New("Failed to execute Java request.")
}
resp_body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
proof := string(resp_body)
gBytes, _ := hexutil.Decode("0x077da99d806abd13c9f15ece5398525119d11e11e9836b2ee7d23f6159ad87d401485efa927f2ad41bff567eec88f32fb0a0f706588b4e41a8d587d008b7f875")
L := make([][2]common.Hash, size)
for i := 0; i < size; i++ {
L_i := new(bn256.G1)
L_i.Unmarshal(append(yBytes[i][0].Bytes(), yBytes[i][1].Bytes()...))
L_i.ScalarMult(L_i, r)
if uint64(i) == index[0] {
gOut := new(bn256.G1)
gOut.Unmarshal(gBytes)
gOut.ScalarMult(gOut, big.NewInt(int64(-bTransfer)))
L_i.Add(L_i, gOut)
} else if uint64(i) == index[1] {
gIn := new(bn256.G1)
gIn.Unmarshal(gBytes)
gIn.ScalarMult(gIn, big.NewInt(int64(bTransfer)))
L_i.Add(L_i, gIn)
}
L[i] = [2]common.Hash{common.BytesToHash(L_i.Marshal()[:32]), common.BytesToHash(L_i.Marshal()[32:])}
}
R := new(bn256.G1)
R.Unmarshal(gBytes)
R.ScalarMult(R, r)
if err != nil {
return nil, err
}
x := new(big.Int)
x.SetBytes(xBytes.Bytes())
u := mapInto("Zether", epoch)
u.ScalarMult(u, x)
result["L"] = L
result["R"] = [2]common.Hash{common.BytesToHash(R.Marshal()[:32]), common.BytesToHash(R.Marshal()[32:])}
result["u"] = [2]common.Hash{common.BytesToHash(u.Marshal()[:32]), common.BytesToHash(u.Marshal()[32:])}
// ^^^ again, all of these extra bits would be unnecessary if elliptic curve ops could be performed directly in javascript.
result["proof"] = proof
return result, nil
}
func (api *PublicZetherAPI) ProveBurn(CL [2]common.Hash, CR [2]common.Hash, y [2]common.Hash, bTransfer uint64, epoch uint64, xBytes common.Hash, bDiff uint64) (map[string]interface{}, error) {
result := make(map[string]interface{})
bTransferBytes := make([]byte, 32)
bDiffBytes := make([]byte, 32)
binary.PutUvarint(bTransferBytes, uint64(bTransfer))
binary.PutUvarint(bDiffBytes, uint64(bDiff))
// consider sending these explicitly as uints instead of bytes
// RPC to Java service
q := url.Values{}
q.Add("CL", hexutil.Encode(append(CL[0].Bytes(), CL[1].Bytes()...)))
q.Add("CR", hexutil.Encode(append(CR[0].Bytes(), CR[1].Bytes()...)))
q.Add("y", hexutil.Encode(append(y[0].Bytes(), y[1].Bytes()...)))
q.Add("bTransfer", hexutil.EncodeUint64(bTransfer))
q.Add("epoch", hexutil.EncodeUint64(epoch))
q.Add("x", hexutil.Encode(xBytes.Bytes()))
q.Add("bDiff", hexutil.EncodeUint64(bDiff))
resp, err := http.PostForm("http://localhost:8080/prove-burn", q)
if err != nil {
return nil, errors.New("Failed to execute Java request.")
}
resp_body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
proof := string(resp_body)
x := new(big.Int)
x.SetBytes(xBytes.Bytes())
u := mapInto("Zether", epoch)
u.ScalarMult(u, x)
result["u"] = [2]common.Hash{common.BytesToHash(u.Marshal()[:32]), common.BytesToHash(u.Marshal()[32:])}
result["proof"] = proof
return result, nil
}