5 Commits
v0.0.1 ... main

Author SHA1 Message Date
arnaucube
9a5be110fc Updt Sequencer errormsg on try_contr on 200ok code 2023-01-14 23:31:21 +01:00
arnaucube
1c95590ba0 Update VerifySRS (SRS/State)
Update/split VerifySRS into verifying new SRS from prev SRS, and
verifying the obtained State from the Sequencer (which includes proofs &
last SRS)
2023-01-14 22:41:48 +01:00
arnaucube
9a50ec9e43 Update queue waiting logic 2023-01-14 10:14:13 +01:00
arnaucube
4c0c9ff781 use different taus for each powers batch 2023-01-04 11:40:46 +01:00
arnaucube
a514115905 Polishing & documenting, add compile script 2022-12-10 10:59:48 +01:00
11 changed files with 368 additions and 198 deletions

7
.gitignore vendored
View File

@@ -1,5 +1,6 @@
bin
parsed_state.json parsed_state.json
new_state.json new_state.json
cmd/prevBatchContribution.json prevBatchContribution.json
cmd/contribution.json contribution.json
cmd/contribution_receipt.json contribution_receipt.json

View File

@@ -13,22 +13,31 @@ This implementation has been done without looking at the other impls code (excep
> This code has not been audited, use it at your own risk. > This code has not been audited, use it at your own risk.
Why in Go? Ideally would have done this code using Rust & arkworks, but the official impl already uses that. Why in Go? Ideally would have done this code using Rust & arkworks, but the official impl already uses that. This implementation uses [Kilic's BLS12-381 pairing implementation](https://github.com/kilic/bls12-381).
Documents used for this implementation: Documents used for this implementation:
- [KZG10-Ceremony-audit-report.pdf, section *3.1 Overview of PoT ceremonies*](https://github.com/ethereum/kzg-ceremony/blob/main/KZG10-Ceremony-audit-report.pdf) - [KZG10-Ceremony-audit-report.pdf, section *3.1 Overview of PoT ceremonies*](https://github.com/ethereum/kzg-ceremony/blob/main/KZG10-Ceremony-audit-report.pdf)
- [*Why and how zkSNARKs work*, by Maksym Petkus](https://arxiv.org/abs/1906.07221v1) - [*Why and how zkSNARKs work*, by Maksym Petkus](https://arxiv.org/abs/1906.07221v1)
**You can find more info on the logic behind the powers of tau computation & verification in these notes: http://arnaucube.com/blog/powersoftau.html**
### Usage ### Usage
Go into the `cmd` dir, and run: Get the binary from the [releases](https://github.com/arnaucube/eth-kzg-ceremony-alt/releases) (alternative you can compile it from source), and run:
``` ```
> go run cmd.go > ./kzgceremony
eth-kzg-ceremony-alt eth-kzg-ceremony-alt
==================== ====================
Usage of /tmp/go-build4278582969/b001/exe/cmd: Usage of ./kzgceremony:
-u, --url string sequencer url (default "https://kzg-ceremony-sequencer-dev.fly.dev") -u, --url string sequencer url (default "https://seq.ceremony.ethereum.org")
-r, --rand string randomness -r, --rand string randomness, needs to be bigger than 64 bytes
-s, --sleeptime uint time (seconds) sleeping before trying again to be the next contributor (default 10) -s, --sleeptime uint time (seconds) sleeping before trying again to be the next contributor (default 30)
``` ```
So for example, run your contribution with:
```
./kzgceremony -r "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod"
```
(where the "Lorem ipsum..." is your source of randomness)

View File

@@ -6,8 +6,10 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"kzgceremony"
"net/http" "net/http"
"strings"
kzgceremony "github.com/arnaucube/eth-kzg-ceremony-alt"
) )
type Client struct { type Client struct {
@@ -131,38 +133,59 @@ func (c *Client) PostAuthCallback() (*MsgRequestLink, error) {
return &msg, err return &msg, err
} }
func (c *Client) PostTryContribute(sessionID string) (*kzgceremony.BatchContribution, bool, error) { type Status int
const (
StatusReauth = Status(iota)
StatusError
StatusWait
StatusProceed
)
func (c *Client) PostTryContribute(sessionID string) (*kzgceremony.BatchContribution, Status, error) {
bearer := "Bearer " + sessionID bearer := "Bearer " + sessionID
resp, err := c.postWithAuth( resp, err := c.postWithAuth(
c.url+"/lobby/try_contribute", "application/json", nil, bearer) c.url+"/lobby/try_contribute", "application/json", nil, bearer)
if err != nil { if err != nil {
return nil, false, err return nil, StatusError, err
} }
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, false, err return nil, StatusError, err
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
fmt.Println(string(body))
switch resp.StatusCode { switch resp.StatusCode {
case http.StatusBadRequest: case http.StatusBadRequest:
return nil, true, fmt.Errorf("call came to early. rate limited") return nil, StatusWait, fmt.Errorf("call came to early. rate limited")
case http.StatusUnauthorized: case http.StatusUnauthorized:
return nil, false, fmt.Errorf("unkown session id. unauthorized access") return nil, StatusReauth, fmt.Errorf("unkown session id. unauthorized access")
default: default:
return nil, false, fmt.Errorf("unexpected http code: %d", resp.StatusCode) return nil, StatusWait, fmt.Errorf("unexpected http code: %d", resp.StatusCode)
} }
} }
// note: a 200 (Ok) code by the Sequencer on try_contribute doesn't
// mean that the contributor has been selected. It could mean that the
// Sequencer is returning the error AnotherContributionInProgress in a
// json msg (see
// https://github.com/ethereum/kzg-ceremony-sequencer/blob/2538f2f08d4db880d7f4608e964df0b695bc7d2f/src/api/v1/error_response.rs#L105
// )
// check if body contains the error message of "another contribution in
// progress" (despite http statuscode being 200 (Ok))
if strings.Contains(string(body), "another contribution in progress") {
return nil, StatusWait, fmt.Errorf("another contribution in progress")
}
err = ioutil.WriteFile("prevBatchContribution.json", body, 0600) err = ioutil.WriteFile("prevBatchContribution.json", body, 0600)
if err != nil { if err != nil {
return nil, false, err return nil, StatusError, err
} }
bc := &kzgceremony.BatchContribution{} bc := &kzgceremony.BatchContribution{}
err = json.Unmarshal(body, bc) err = json.Unmarshal(body, bc)
return bc, false, err return bc, StatusProceed, err
} }
func (c *Client) PostAbortContribution(sessionID string) ([]byte, error) { func (c *Client) PostAbortContribution(sessionID string) ([]byte, error) {

View File

@@ -5,88 +5,67 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"kzgceremony"
"kzgceremony/client"
"os" "os"
"strings" "strings"
"time" "time"
kzgceremony "github.com/arnaucube/eth-kzg-ceremony-alt"
"github.com/arnaucube/eth-kzg-ceremony-alt/client"
"github.com/fatih/color" "github.com/fatih/color"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
) )
type Config struct { var (
sequencerURL string red = color.New(color.FgRed)
randomness string redB = color.New(color.FgRed, color.Bold)
sleepTime uint64 cyan = color.New(color.FgCyan)
} cyanB = color.New(color.FgCyan, color.Bold)
green = color.New(color.FgHiGreen)
greenB = color.New(color.FgHiGreen, color.Bold)
)
func main() { func main() {
fmt.Println("eth-kzg-ceremony-alt") fmt.Println("eth-kzg-ceremony-alt")
fmt.Printf("====================\n\n") fmt.Printf("====================\n")
fmt.Printf(" https://github.com/arnaucube/eth-kzg-ceremony-alt\n\n")
red := color.New(color.FgRed) var sequencerURL string
redB := color.New(color.FgRed, color.Bold) var randomness string
cyan := color.New(color.FgCyan) var sleepTime uint64
cyanB := color.New(color.FgCyan, color.Bold) flag.StringVarP(&sequencerURL, "url", "u",
green := color.New(color.FgHiGreen) "https://seq.ceremony.ethereum.org", "sequencer url")
greenB := color.New(color.FgHiGreen, color.Bold) flag.StringVarP(&randomness, "rand", "r",
"", fmt.Sprintf("randomness, needs to be bigger than %d bytes", kzgceremony.MinRandomnessLen))
config := Config{} flag.Uint64VarP(&sleepTime, "sleeptime", "s",
flag.StringVarP(&config.sequencerURL, "url", "u", 30, "time (seconds) sleeping before trying again to be the next contributor")
"https://kzg-ceremony-sequencer-dev.fly.dev", "sequencer url")
flag.StringVarP(&config.randomness, "rand", "r",
"", "randomness")
flag.Uint64VarP(&config.sleepTime, "sleeptime", "s",
10, "time (seconds) sleeping before trying again to be the next contributor")
flag.CommandLine.SortFlags = false flag.CommandLine.SortFlags = false
flag.Parse() flag.Parse()
c := client.NewClient(config.sequencerURL) c := client.NewClient(sequencerURL)
// get status // get status
msgStatus, err := c.GetCurrentStatus() msgStatus, err := c.GetCurrentStatus()
if err != nil { if err != nil {
red.Println(err) printErrAndExit(err)
os.Exit(1)
} }
fmt.Println(msgStatus) fmt.Println(msgStatus)
if config.randomness == "" { if randomness == "" {
_, _ =
cyanB.Println("To contribute to the ceremony, please set your randomness. Use -h to show the available flags.") cyanB.Println("To contribute to the ceremony, please set your randomness. Use -h to show the available flags.")
os.Exit(0) os.Exit(0)
} }
if len([]byte(config.randomness)) < kzgceremony.MinRandomnessLen { if len([]byte(randomness)) < kzgceremony.MinRandomnessLen {
redB.Printf("Randomness must be longer than %d, current length: %d\n", _, _ = redB.Printf("Randomness must be longer than %d, current length: %d\n",
kzgceremony.MinRandomnessLen, len([]byte(config.randomness))) kzgceremony.MinRandomnessLen, len([]byte(randomness)))
os.Exit(1) os.Exit(1)
} }
// Auth // Auth
msgReqLink, err := c.GetRequestLink() fmt.Println("Github Authorization:")
if err != nil { authMsg := authGH(c)
red.Println(err)
os.Exit(1)
}
green.Printf("Please go to\n%s\n and authenticate with Github.\n", msgReqLink.GithubAuthURL)
fmt.Println("(currently only Github auth is supported)")
greenB.Printf("Paste here the RawData from the auth answer:\n")
s, err := readInput()
if err != nil {
red.Println(err)
os.Exit(1)
}
var authMsg client.MsgAuthCallback
if err = json.Unmarshal([]byte(s), &authMsg); err != nil {
red.Println(err)
os.Exit(1)
}
fmt.Print("Parsed auth msg: ")
cyan.Printf("%#v\n", authMsg)
// TODO this will be only triggered by a flag // TODO this will be only triggered by a flag
// msg, err := c.PostAbortContribution(authMsg.SessionID) // msg, err := c.PostAbortContribution(authMsg.SessionID)
@@ -101,17 +80,28 @@ func main() {
var prevBatchContribution *kzgceremony.BatchContribution var prevBatchContribution *kzgceremony.BatchContribution
for { for {
fmt.Printf("%s sending try_contribute\n", time.Now().Format("2006-01-02 15:04:05")) fmt.Printf("%s sending try_contribute\n", time.Now().Format("2006-01-02 15:04:05"))
var retry bool var status client.Status
prevBatchContribution, retry, err = c.PostTryContribute(authMsg.SessionID) prevBatchContribution, status, err = c.PostTryContribute(authMsg.SessionID)
if err != nil { if err != nil {
red.Println(err) _, _ = cyan.Println(err)
} }
if !retry { if status == client.StatusProceed {
break break
} }
fmt.Printf("%s try_contribute unsuccessful, going to sleep %d seconds\n", if status == client.StatusReauth {
time.Now().Format("2006-01-02 15:04:05"), config.sleepTime) fmt.Println("SessionID has expired, authenticate again with Github:")
time.Sleep(time.Duration(config.sleepTime) * time.Second) authMsg = authGH(c)
}
msgStatus, err := c.GetCurrentStatus()
if err != nil {
printErrAndExit(err)
}
fmt.Printf("%s try_contribute unsuccessful, lobby size %d, num contrib %d,"+
"\n going to sleep %d seconds\n",
time.Now().Format("2006-01-02 15:04:05"),
msgStatus.LobbySize, msgStatus.NumContributions,
sleepTime)
time.Sleep(time.Duration(sleepTime) * time.Second)
} }
// get latest state // get latest state
@@ -122,48 +112,76 @@ func main() {
// } // }
fmt.Println("starting to compute new contribution") fmt.Println("starting to compute new contribution")
newBatchContribution, err := prevBatchContribution.Contribute([]byte(config.randomness)) t0 := time.Now()
newBatchContribution, err := prevBatchContribution.Contribute([]byte(randomness))
if err != nil { if err != nil {
red.Println(err) fmt.Println("error on prevBatchContribution.Contribute")
os.Exit(1) printErrAndExit(err)
} }
fmt.Println("Contribution computed in", time.Since(t0))
// store contribution // store contribution
fmt.Println("storing contribution.json") fmt.Println("storing contribution.json")
b, err := json.Marshal(newBatchContribution) b, err := json.Marshal(newBatchContribution)
if err != nil { if err != nil {
red.Println(err) // print error but do not exit
os.Exit(1) _, _ = red.Println(err)
} }
err = ioutil.WriteFile("contribution.json", b, 0600) err = ioutil.WriteFile("contribution.json", b, 0600)
if err != nil { if err != nil {
red.Println(err) // print error but do not exit
os.Exit(1) _, _ = red.Println(err)
} }
// send contribution // send contribution
fmt.Println("sending contribution") fmt.Println("sending contribution")
receipt, err := c.PostContribute(authMsg.SessionID, newBatchContribution) receipt, err := c.PostContribute(authMsg.SessionID, newBatchContribution)
if err != nil { if err != nil {
red.Println(err) printErrAndExit(err)
os.Exit(1)
} }
fmt.Println("Receipt:") fmt.Println("Receipt:")
green.Println(receipt) _, _ = green.Println(receipt)
// store receipt // store receipt
fmt.Println("storing contribution_receipt.json") fmt.Println("storing contribution_receipt.json")
b, err = json.Marshal(receipt) b, err = json.Marshal(receipt)
if err != nil { if err != nil {
red.Println(err) printErrAndExit(err)
os.Exit(1)
} }
err = ioutil.WriteFile("contribution_receipt.json", b, 0600) err = ioutil.WriteFile("contribution_receipt.json", b, 0600)
if err != nil { if err != nil {
red.Println(err) printErrAndExit(err)
os.Exit(1)
} }
} }
func authGH(c *client.Client) client.MsgAuthCallback {
msgReqLink, err := c.GetRequestLink()
if err != nil {
printErrAndExit(err)
}
_, _ = green.Printf("Please go to\n%s\n and authenticate with Github.\n", msgReqLink.GithubAuthURL)
fmt.Println("(currently only Github auth is supported)")
_, _ = greenB.Printf("Paste here the RawData from the auth answer:\n")
s, err := readInput()
if err != nil {
printErrAndExit(err)
}
var authMsg client.MsgAuthCallback
if err = json.Unmarshal([]byte(s), &authMsg); err != nil {
printErrAndExit(err)
}
fmt.Print("Parsed auth msg: ")
_, _ = cyan.Printf("%#v\n", authMsg)
return authMsg
}
func printErrAndExit(err error) {
_, _ = red.Println(err)
os.Exit(1)
}
func readInput() (string, error) { func readInput() (string, error) {
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n') input, err := reader.ReadString('\n')

14
compile.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
mkdir -p bin
cd cmd
echo "building linux binaries"
GOOS=linux GOARCH=amd64 go build -o ../bin/kzgceremony-amd64-linux *.go
echo "building windows binaries"
GOOS=windows GOARCH=amd64 go build -o ../bin/kzgceremony-amd64.exe *.go
echo "building macOS binaries"
GOOS=darwin GOARCH=amd64 go build -o ../bin/kzgceremony-amd64-darwin *.go

5
go.mod
View File

@@ -1,4 +1,4 @@
module kzgceremony module github.com/arnaucube/eth-kzg-ceremony-alt
go 1.19 go 1.19
@@ -7,6 +7,7 @@ require (
github.com/frankban/quicktest v1.14.4 github.com/frankban/quicktest v1.14.4
github.com/kilic/bls12-381 v0.1.0 github.com/kilic/bls12-381 v0.1.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
golang.org/x/crypto v0.4.0
) )
require ( require (
@@ -16,5 +17,5 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/sys v0.3.0 // indirect
) )

6
go.sum
View File

@@ -22,10 +22,12 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -9,6 +9,8 @@ import (
bls12381 "github.com/kilic/bls12-381" bls12381 "github.com/kilic/bls12-381"
) )
// UnmarshalJSON implements the State json unmarshaler, compatible
// with the official Ethereum KZG Ceremony formats
func (s *State) UnmarshalJSON(b []byte) error { func (s *State) UnmarshalJSON(b []byte) error {
var sStr stateStr var sStr stateStr
if err := json.Unmarshal(b, &sStr); err != nil { if err := json.Unmarshal(b, &sStr); err != nil {
@@ -57,10 +59,11 @@ func (s *State) UnmarshalJSON(b []byte) error {
return err return err
} }
} }
// TODO validate data (G1 & G2 subgroup checks, etc)
return err return err
} }
// MarshalJSON implements the State json marshaler, compatible with the
// official Ethereum KZG Ceremony formats
func (s State) MarshalJSON() ([]byte, error) { func (s State) MarshalJSON() ([]byte, error) {
var sStr stateStr var sStr stateStr
sStr.ParticipantIDs = s.ParticipantIDs sStr.ParticipantIDs = s.ParticipantIDs
@@ -93,13 +96,14 @@ func (s State) MarshalJSON() ([]byte, error) {
return json.Marshal(sStr) return json.Marshal(sStr)
} }
// UnmarshalJSON implements the BatchContribution json unmarshaler, compatible
// with the official Ethereum KZG Ceremony formats
func (c *BatchContribution) UnmarshalJSON(b []byte) error { func (c *BatchContribution) UnmarshalJSON(b []byte) error {
var cStr batchContributionStr var cStr batchContributionStr
if err := json.Unmarshal(b, &cStr); err != nil { if err := json.Unmarshal(b, &cStr); err != nil {
return err return err
} }
var err error var err error
g2 := bls12381.NewG2()
c.Contributions = make([]Contribution, len(cStr.Contributions)) c.Contributions = make([]Contribution, len(cStr.Contributions))
for i := 0; i < len(cStr.Contributions); i++ { for i := 0; i < len(cStr.Contributions); i++ {
@@ -129,9 +133,10 @@ func (c *BatchContribution) UnmarshalJSON(b []byte) error {
return err return err
} }
// MarshalJSON implements the BatchContribution json marshaler, compatible
// with the official Ethereum KZG Ceremony formats
func (c BatchContribution) MarshalJSON() ([]byte, error) { func (c BatchContribution) MarshalJSON() ([]byte, error) {
var cStr batchContributionStr var cStr batchContributionStr
g2 := bls12381.NewG2()
cStr.Contributions = make([]contributionStr, len(c.Contributions)) cStr.Contributions = make([]contributionStr, len(c.Contributions))
for i := 0; i < len(c.Contributions); i++ { for i := 0; i < len(c.Contributions); i++ {
@@ -143,7 +148,8 @@ func (c BatchContribution) MarshalJSON() ([]byte, error) {
cStr.Contributions[i].PowersOfTau.G2Powers = cStr.Contributions[i].PowersOfTau.G2Powers =
g2PointsToStrings(c.Contributions[i].PowersOfTau.G2Powers) g2PointsToStrings(c.Contributions[i].PowersOfTau.G2Powers)
cStr.Contributions[i].PotPubKey = "0x" + hex.EncodeToString(g2.ToCompressed(c.Contributions[i].PotPubKey)) cStr.Contributions[i].PotPubKey = "0x" +
hex.EncodeToString(g2.ToCompressed(c.Contributions[i].PotPubKey))
} }
return json.Marshal(cStr) return json.Marshal(cStr)
} }
@@ -184,7 +190,6 @@ type stateStr struct {
} }
func g1PointsToStrings(points []*bls12381.PointG1) []string { func g1PointsToStrings(points []*bls12381.PointG1) []string {
g1 := bls12381.NewG1() // TODO unify g1 instantiation (& g2)
n := len(points) n := len(points)
g1s := make([]string, n) g1s := make([]string, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
@@ -198,7 +203,6 @@ func g1PointsToStrings(points []*bls12381.PointG1) []string {
} }
func g2PointsToStrings(points []*bls12381.PointG2) []string { func g2PointsToStrings(points []*bls12381.PointG2) []string {
g2 := bls12381.NewG2()
n := len(points) n := len(points)
g2s := make([]string, n) g2s := make([]string, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
@@ -211,8 +215,10 @@ func g2PointsToStrings(points []*bls12381.PointG2) []string {
return g2s return g2s
} }
// stringsToPointsG1 parses the strings that represent the G1 points in the
// ZCash compressed format into bls12381.PointG1 data structure. Additionally
// it checks the points correctness
func stringsToPointsG1(s []string) ([]*bls12381.PointG1, error) { func stringsToPointsG1(s []string) ([]*bls12381.PointG1, error) {
g1 := bls12381.NewG1() // TODO unify g1 instantiation (& g2)
n := len(s) n := len(s)
g1s := make([]*bls12381.PointG1, n) g1s := make([]*bls12381.PointG1, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
@@ -227,12 +233,18 @@ func stringsToPointsG1(s []string) ([]*bls12381.PointG1, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := checkG1PointCorrectness(g1s_i); err != nil {
return nil, err
}
g1s[i] = g1s_i g1s[i] = g1s_i
} }
return g1s, nil return g1s, nil
} }
// stringsToPointsG2 parses the strings that represent the G2 points in the
// ZCash compressed format into bls12381.PointG2 data structure. Additionally
// it checks the points correctness
func stringsToPointsG2(s []string) ([]*bls12381.PointG2, error) { func stringsToPointsG2(s []string) ([]*bls12381.PointG2, error) {
g2 := bls12381.NewG2()
n := len(s) n := len(s)
g2s := make([]*bls12381.PointG2, n) g2s := make([]*bls12381.PointG2, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
@@ -247,6 +259,9 @@ func stringsToPointsG2(s []string) ([]*bls12381.PointG2, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := checkG2PointCorrectness(g2s_i); err != nil {
return nil, err
}
g2s[i] = g2s_i g2s[i] = g2s_i
} }
return g2s, nil return g2s, nil

View File

@@ -35,6 +35,7 @@ func TestParseCompressedG1Point(t *testing.T) {
p1Str := "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" p1Str := "0x97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"
g1Bytes, err := hex.DecodeString(strings.TrimPrefix(p1Str, "0x")) g1Bytes, err := hex.DecodeString(strings.TrimPrefix(p1Str, "0x"))
c.Assert(err, qt.IsNil)
g1Point, err := g1.FromCompressed(g1Bytes) g1Point, err := g1.FromCompressed(g1Bytes)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@@ -43,6 +44,7 @@ func TestParseCompressedG1Point(t *testing.T) {
p1Str = "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" p1Str = "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
g1Bytes, err = hex.DecodeString(strings.TrimPrefix(p1Str, "0x")) g1Bytes, err = hex.DecodeString(strings.TrimPrefix(p1Str, "0x"))
c.Assert(err, qt.IsNil)
g1Point, err = g1.FromCompressed(g1Bytes) g1Point, err = g1.FromCompressed(g1Bytes)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@@ -61,6 +63,7 @@ func TestParseCompressedG2Point(t *testing.T) {
p2Str := "0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8" p2Str := "0x93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"
g2Bytes, err := hex.DecodeString(strings.TrimPrefix(p2Str, "0x")) g2Bytes, err := hex.DecodeString(strings.TrimPrefix(p2Str, "0x"))
c.Assert(err, qt.IsNil)
g2Point, err := g2.FromCompressed(g2Bytes) g2Point, err := g2.FromCompressed(g2Bytes)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
@@ -69,6 +72,7 @@ func TestParseCompressedG2Point(t *testing.T) {
p2Str = "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" p2Str = "0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
g2Bytes, err = hex.DecodeString(strings.TrimPrefix(p2Str, "0x")) g2Bytes, err = hex.DecodeString(strings.TrimPrefix(p2Str, "0x"))
c.Assert(err, qt.IsNil)
g2Point, err = g2.FromCompressed(g2Bytes) g2Point, err = g2.FromCompressed(g2Bytes)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)

View File

@@ -4,17 +4,42 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"golang.org/x/crypto/blake2b"
bls12381 "github.com/kilic/bls12-381" bls12381 "github.com/kilic/bls12-381"
) )
// todo: unify addition & multiplicative notation in the comments // MinRandomnessLen is the minimum accepted length for the user defined
// randomness
const MinRandomnessLen = 64 const MinRandomnessLen = 64
type Witness struct { var g1 *bls12381.G1
RunningProducts []*bls12381.PointG1 var g2 *bls12381.G2
PotPubKeys []*bls12381.PointG2
BLSSignatures []*bls12381.PointG1 func init() {
g1 = bls12381.NewG1()
g2 = bls12381.NewG2()
}
// State represents the data structure obtained from the Sequencer at the
// /info/current_state endpoint
type State struct {
Transcripts []Transcript
ParticipantIDs []string // WIP
ParticipantECDSASignatures []string
}
// BatchContribution represents the data structure obtained from the Sequencer
// at the /contribute endpoint
type BatchContribution struct {
Contributions []Contribution
}
type Contribution struct {
NumG1Powers uint64
NumG2Powers uint64
PowersOfTau *SRS
PotPubKey *bls12381.PointG2
} }
type Transcript struct { type Transcript struct {
@@ -24,22 +49,33 @@ type Transcript struct {
Witness *Witness Witness *Witness
} }
type State struct { type Witness struct {
Transcripts []Transcript RunningProducts []*bls12381.PointG1
ParticipantIDs []string // WIP PotPubKeys []*bls12381.PointG2
ParticipantECDSASignatures []string BLSSignatures []*bls12381.PointG1
} }
type Contribution struct { // SRS contains the powers of tau in G1 & G2, eg.
NumG1Powers uint64 // [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁,
NumG2Powers uint64 // [τ'⁰]₂, [τ'¹]₂, [τ'²]₂, ..., [τ'ⁿ⁻¹]₂
PowersOfTau *SRS type SRS struct {
PotPubKey *bls12381.PointG2 G1Powers []*bls12381.PointG1
} G2Powers []*bls12381.PointG2
type BatchContribution struct {
Contributions []Contribution
} }
type toxicWaste struct {
tau *big.Int
TauG2 *bls12381.PointG2 // Proof.G2P
}
// Proof contains g₂ᵖ and g₂^τ', used by the verifier
type Proof struct {
G2P *bls12381.PointG2 // g₂ᵖ
G1PTau *bls12381.PointG1 // g₂^τ' = g₂^{p ⋅ τ}
}
// Contribute takes the last State and computes a new State using the defined
// randomness
func (cs *State) Contribute(randomness []byte) (*State, error) { func (cs *State) Contribute(randomness []byte) (*State, error) {
ns := State{} ns := State{}
ns.Transcripts = make([]Transcript, len(cs.Transcripts)) ns.Transcripts = make([]Transcript, len(cs.Transcripts))
@@ -47,7 +83,7 @@ func (cs *State) Contribute(randomness []byte) (*State, error) {
ns.Transcripts[i].NumG1Powers = cs.Transcripts[i].NumG1Powers ns.Transcripts[i].NumG1Powers = cs.Transcripts[i].NumG1Powers
ns.Transcripts[i].NumG2Powers = cs.Transcripts[i].NumG2Powers ns.Transcripts[i].NumG2Powers = cs.Transcripts[i].NumG2Powers
newSRS, proof, err := Contribute(cs.Transcripts[i].PowersOfTau, randomness) newSRS, proof, err := Contribute(cs.Transcripts[i].PowersOfTau, i, randomness)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -66,6 +102,8 @@ func (cs *State) Contribute(randomness []byte) (*State, error) {
return &ns, nil return &ns, nil
} }
// Contribute takes the last BatchContribution and computes a new
// BatchContribution using the defined randomness
func (pb *BatchContribution) Contribute(randomness []byte) (*BatchContribution, error) { func (pb *BatchContribution) Contribute(randomness []byte) (*BatchContribution, error) {
nb := BatchContribution{} nb := BatchContribution{}
nb.Contributions = make([]Contribution, len(pb.Contributions)) nb.Contributions = make([]Contribution, len(pb.Contributions))
@@ -73,7 +111,7 @@ func (pb *BatchContribution) Contribute(randomness []byte) (*BatchContribution,
nb.Contributions[i].NumG1Powers = pb.Contributions[i].NumG1Powers nb.Contributions[i].NumG1Powers = pb.Contributions[i].NumG1Powers
nb.Contributions[i].NumG2Powers = pb.Contributions[i].NumG2Powers nb.Contributions[i].NumG2Powers = pb.Contributions[i].NumG2Powers
newSRS, proof, err := Contribute(pb.Contributions[i].PowersOfTau, randomness) newSRS, proof, err := Contribute(pb.Contributions[i].PowersOfTau, i, randomness)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -85,48 +123,24 @@ func (pb *BatchContribution) Contribute(randomness []byte) (*BatchContribution,
return &nb, nil return &nb, nil
} }
// SRS contains the powers of tau in G1 & G2, eg. // newEmptySRS creates an empty SRS filled by [1]₁ & [1]₂ points in all
// [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁, // respective arrays positions
// [τ'⁰]₂, [τ'¹]₂, [τ'²]₂, ..., [τ'ⁿ⁻¹]₂
type SRS struct {
G1Powers []*bls12381.PointG1
G2Powers []*bls12381.PointG2
}
type toxicWaste struct {
tau *big.Int
TauG2 *bls12381.PointG2
}
// Proof contains g₂ᵖ and g₂^τ', used by the verifier
type Proof struct {
G2P *bls12381.PointG2 // g₂ᵖ
G1PTau *bls12381.PointG1 // g₂^τ' = g₂^{p ⋅ τ}
}
// newEmptySRS creates an empty SRS
func newEmptySRS(nG1, nG2 int) *SRS { func newEmptySRS(nG1, nG2 int) *SRS {
g1s := make([]*bls12381.PointG1, nG1) g1s := make([]*bls12381.PointG1, nG1)
g2s := make([]*bls12381.PointG2, nG2) g2s := make([]*bls12381.PointG2, nG2)
g1 := bls12381.NewG1()
g2 := bls12381.NewG2()
// one_G1 := g1.One()
// one_G2 := g2.One()
for i := 0; i < nG1; i++ { for i := 0; i < nG1; i++ {
g1s[i] = g1.One() g1s[i] = g1.One()
// g1.MulScalar(g1s[i], one_G1, big.NewInt(int64(i)))
} }
for i := 0; i < nG2; i++ { for i := 0; i < nG2; i++ {
g2s[i] = g2.One() g2s[i] = g2.One()
// g2.MulScalar(g2s[i], one_G2, big.NewInt(int64(i)))
} }
return &SRS{g1s, g2s} return &SRS{g1s, g2s}
} }
func tau(randomness []byte) *toxicWaste { func tau(round int, randomness []byte) *toxicWaste {
g2 := bls12381.NewG2() val := blake2b.Sum256(append(randomness, byte(round)))
tau := new(big.Int).Mod( tau := new(big.Int).Mod(
new(big.Int).SetBytes(randomness), new(big.Int).SetBytes(val[:]),
g2.Q()) g2.Q())
tau_Fr := bls12381.NewFr().FromBytes(tau.Bytes()) tau_Fr := bls12381.NewFr().FromBytes(tau.Bytes())
TauG2 := g2.New() TauG2 := g2.New()
@@ -137,8 +151,6 @@ func tau(randomness []byte) *toxicWaste {
func computeContribution(t *toxicWaste, prevSRS *SRS) *SRS { func computeContribution(t *toxicWaste, prevSRS *SRS) *SRS {
srs := newEmptySRS(len(prevSRS.G1Powers), len(prevSRS.G2Powers)) srs := newEmptySRS(len(prevSRS.G1Powers), len(prevSRS.G2Powers))
g1 := bls12381.NewG1()
g2 := bls12381.NewG2()
Q := g1.Q() // Q = |G1| == |G2| Q := g1.Q() // Q = |G1| == |G2|
// fmt.Println("Computing [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁, for n =", len(prevSRS.G1s)) // fmt.Println("Computing [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁, for n =", len(prevSRS.G1s))
@@ -158,7 +170,6 @@ func computeContribution(t *toxicWaste, prevSRS *SRS) *SRS {
} }
func genProof(toxicWaste *toxicWaste, prevSRS, newSRS *SRS) *Proof { func genProof(toxicWaste *toxicWaste, prevSRS, newSRS *SRS) *Proof {
g1 := bls12381.NewG1()
G1_p := g1.New() G1_p := g1.New()
tau_Fr := bls12381.NewFr().FromBytes(toxicWaste.tau.Bytes()) tau_Fr := bls12381.NewFr().FromBytes(toxicWaste.tau.Bytes())
g1.MulScalar(G1_p, prevSRS.G1Powers[1], tau_Fr) // g_1^{tau'} = g_1^{p * tau}, where p=toxicWaste.tau g1.MulScalar(G1_p, prevSRS.G1Powers[1], tau_Fr) // g_1^{tau'} = g_1^{p * tau}, where p=toxicWaste.tau
@@ -168,12 +179,13 @@ func genProof(toxicWaste *toxicWaste, prevSRS, newSRS *SRS) *Proof {
// Contribute takes as input the previous SRS and a random // Contribute takes as input the previous SRS and a random
// byte slice, and returns the new SRS together with the Proof // byte slice, and returns the new SRS together with the Proof
func Contribute(prevSRS *SRS, randomness []byte) (*SRS, *Proof, error) { func Contribute(prevSRS *SRS, round int, randomness []byte) (*SRS, *Proof, error) {
if len(randomness) < MinRandomnessLen { if len(randomness) < MinRandomnessLen {
return nil, nil, fmt.Errorf("err randomness") // WIP return nil, nil, fmt.Errorf("err: randomness length < %d",
MinRandomnessLen)
} }
// set tau from randomness // set tau from randomness
tw := tau(randomness) tw := tau(round, randomness)
newSRS := computeContribution(tw, prevSRS) newSRS := computeContribution(tw, prevSRS)
@@ -182,45 +194,58 @@ func Contribute(prevSRS *SRS, randomness []byte) (*SRS, *Proof, error) {
return newSRS, proof, nil return newSRS, proof, nil
} }
// Verify checks the correct computation of the new SRS respectively from the func checkG1PointCorrectness(p *bls12381.PointG1) error {
// previous SRS // i) non-empty
func Verify(prevSRS, newSRS *SRS, proof *Proof) bool { if p == nil {
g1 := bls12381.NewG1() return fmt.Errorf("empty point value")
g2 := bls12381.NewG2() }
// ii) non-zero
if g1.IsZero(p) {
return fmt.Errorf("point can not be zero")
}
// iii) in the correct prime order of subgroups
if !g1.IsOnCurve(p) {
return fmt.Errorf("point not on curve")
}
if !g1.InCorrectSubgroup(p) {
return fmt.Errorf("point not in the correct prime order of subgroups")
}
return nil
}
func checkG2PointCorrectness(p *bls12381.PointG2) error {
// i) non-empty
if p == nil {
return fmt.Errorf("empty point value")
}
// ii) non-zero
if g2.IsZero(p) {
return fmt.Errorf("point can not be zero")
}
// iii) in the correct prime order of subgroups
if !g2.IsOnCurve(p) {
return fmt.Errorf("point not on curve")
}
if !g2.InCorrectSubgroup(p) {
return fmt.Errorf("point not in the correct prime order of subgroups")
}
return nil
}
// VerifyNewSRSFromPrevSRS checks the correct computation of the new SRS
// respectively from the previous SRS. These are the checks that the Sequencer
// would do.
func VerifyNewSRSFromPrevSRS(prevSRS, newSRS *SRS, proof *Proof) bool {
pairing := bls12381.NewEngine() pairing := bls12381.NewEngine()
// 1. check that elements of the newSRS are valid points // 1. check that elements of the newSRS are valid points
for i := 0; i < len(newSRS.G1Powers); i++ { for i := 0; i < len(newSRS.G1Powers); i++ {
// i) non-empty if err := checkG1PointCorrectness(newSRS.G1Powers[i]); err != nil {
if newSRS.G1Powers[i] == nil {
return false
}
// ii) non-zero
if g1.IsZero(newSRS.G1Powers[i]) {
return false
}
// iii) in the correct prime order of subgroups
if !g1.IsOnCurve(newSRS.G1Powers[i]) {
return false
}
if !g1.InCorrectSubgroup(newSRS.G1Powers[i]) {
return false return false
} }
} }
for i := 0; i < len(newSRS.G2Powers); i++ { for i := 0; i < len(newSRS.G2Powers); i++ {
// i) non-empty if err := checkG2PointCorrectness(newSRS.G2Powers[i]); err != nil {
if newSRS.G2Powers[i] == nil {
return false
}
// ii) non-zero
if g2.IsZero(newSRS.G2Powers[i]) {
return false
}
// iii) in the correct prime order of subgroups
if !g2.IsOnCurve(newSRS.G2Powers[i]) {
return false
}
if !g2.InCorrectSubgroup(newSRS.G2Powers[i]) {
return false return false
} }
} }
@@ -259,3 +284,58 @@ func Verify(prevSRS, newSRS *SRS, proof *Proof) bool {
return true return true
} }
// VerifyState acts similarly to VerifyNewSRSFromPrevSRS, but verifying the
// given State (which can be obtained from the Sequencer)
func VerifyState(s *State) bool {
pairing := bls12381.NewEngine()
for _, t := range s.Transcripts {
// 1. check that elements of the SRS are valid points
for i := 0; i < len(t.PowersOfTau.G1Powers); i++ {
if err := checkG1PointCorrectness(t.PowersOfTau.G1Powers[i]); err != nil {
return false
}
}
for i := 0; i < len(t.PowersOfTau.G2Powers); i++ {
if err := checkG2PointCorrectness(t.PowersOfTau.G2Powers[i]); err != nil {
return false
}
}
// 2. check t.Witness.RunningProducts[last] == t.PowersOfTau.G1Powers[1]
if !g1.Equal(t.Witness.RunningProducts[len(t.Witness.RunningProducts)-1],
t.PowersOfTau.G1Powers[1]) {
return false
}
// 3. check newSRS.G1s[1] (g₁^τ'), is correctly related to prevSRS.G1s[1] (g₁^τ)
// e([τ]₁, [p]₂) == e([τ']₁, [1]₂)
eL := pairing.AddPair(t.Witness.RunningProducts[len(t.Witness.RunningProducts)-2], t.Witness.PotPubKeys[len(t.Witness.PotPubKeys)-1]).Result()
eR := pairing.AddPair(t.Witness.RunningProducts[len(t.Witness.RunningProducts)-1], g2.One()).Result()
if !eL.Equal(eR) {
return false
}
// 4. check newSRS following the powers of tau structure
for i := 0; i < len(t.PowersOfTau.G1Powers)-1; i++ {
// i) e([τ'ⁱ]₁, [τ']₂) == e([τ'ⁱ⁺¹]₁, [1]₂), for i ∈ [1, n1]
eL := pairing.AddPair(t.PowersOfTau.G1Powers[i], t.PowersOfTau.G2Powers[1]).Result()
eR := pairing.AddPair(t.PowersOfTau.G1Powers[i+1], g2.One()).Result()
if !eL.Equal(eR) {
return false
}
}
for i := 0; i < len(t.PowersOfTau.G2Powers)-1; i++ {
// ii) e([τ']₁, [τ'ʲ]₂) == e([1]₁, [τ'ʲ⁺¹]₂), for j ∈ [1, m1]
eL := pairing.AddPair(t.PowersOfTau.G1Powers[1], t.PowersOfTau.G2Powers[i]).Result()
eR := pairing.AddPair(g1.One(), t.PowersOfTau.G2Powers[i+1]).Result()
if !eL.Equal(eR) {
return false
}
}
}
return true
}

View File

@@ -2,7 +2,6 @@ package kzgceremony
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"testing" "testing"
@@ -14,16 +13,16 @@ func TestContribution(t *testing.T) {
srs_0 := newEmptySRS(10, 10) srs_0 := newEmptySRS(10, 10)
srs_1, proof_1, err := Contribute(srs_0, srs_1, proof_1, err := Contribute(srs_0, 0,
[]byte("1111111111111111111111111111111111111111111111111111111111111111")) []byte("1111111111111111111111111111111111111111111111111111111111111111"))
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
c.Assert(Verify(srs_0, srs_1, proof_1), qt.IsTrue) c.Assert(VerifyNewSRSFromPrevSRS(srs_0, srs_1, proof_1), qt.IsTrue)
srs_2, proof_2, err := Contribute(srs_1, srs_2, proof_2, err := Contribute(srs_1, 0,
[]byte("2222222222222222222222222222222222222222222222222222222222222222")) []byte("2222222222222222222222222222222222222222222222222222222222222222"))
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
c.Assert(Verify(srs_1, srs_2, proof_2), qt.IsTrue) c.Assert(VerifyNewSRSFromPrevSRS(srs_1, srs_2, proof_2), qt.IsTrue)
} }
func TestComputeNewState(t *testing.T) { func TestComputeNewState(t *testing.T) {
@@ -58,7 +57,11 @@ func TestBatchContribution(t *testing.T) {
bc.Contribute([]byte("1111111111111111111111111111111111111111111111111111111111111111")) bc.Contribute([]byte("1111111111111111111111111111111111111111111111111111111111111111"))
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
b, err := json.Marshal(nb) c.Assert(len(nb.Contributions), qt.Equals, 4)
c.Assert(g2.Equal(nb.Contributions[0].PotPubKey, nb.Contributions[1].PotPubKey), qt.IsFalse)
c.Assert(g2.Equal(nb.Contributions[0].PotPubKey, nb.Contributions[2].PotPubKey), qt.IsFalse)
c.Assert(g2.Equal(nb.Contributions[0].PotPubKey, nb.Contributions[3].PotPubKey), qt.IsFalse)
_, err = json.Marshal(nb)
c.Assert(err, qt.IsNil) c.Assert(err, qt.IsNil)
fmt.Println(string(b))
} }