mirror of https://github.com/poanetwork/quorum.git
245 lines
8.3 KiB
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
|
|
}
|