Browse Source

Polishing & documenting, add compile script

main
arnaucube 1 year ago
parent
commit
a514115905
10 changed files with 191 additions and 140 deletions
  1. +4
    -3
      .gitignore
  2. +10
    -4
      README.md
  3. +2
    -1
      client/req.go
  4. +40
    -48
      cmd/cmd.go
  5. +14
    -0
      compile.sh
  6. +1
    -1
      go.mod
  7. +23
    -8
      parsers.go
  8. +4
    -0
      parsers_test.go
  9. +92
    -72
      powersoftau.go
  10. +1
    -3
      powersoftau_test.go

+ 4
- 3
.gitignore

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

+ 10
- 4
README.md

@ -13,22 +13,28 @@ 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.
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:
- [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)
### 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
====================
Usage of /tmp/go-build4278582969/b001/exe/cmd:
Usage of ./kzgceremony:
-u, --url string sequencer url (default "https://kzg-ceremony-sequencer-dev.fly.dev")
-r, --rand string randomness
-s, --sleeptime uint time (seconds) sleeping before trying again to be the next contributor (default 10)
```
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)

+ 2
- 1
client/req.go

@ -6,8 +6,9 @@ import (
"fmt"
"io"
"io/ioutil"
"kzgceremony"
"net/http"
kzgceremony "github.com/arnaucube/eth-kzg-ceremony-alt"
)
type Client struct {

+ 40
- 48
cmd/cmd.go

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

+ 14
- 0
compile.sh

@ -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

+ 1
- 1
go.mod

@ -1,4 +1,4 @@
module kzgceremony
module github.com/arnaucube/eth-kzg-ceremony-alt
go 1.19

+ 23
- 8
parsers.go

@ -9,6 +9,8 @@ import (
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 {
var sStr stateStr
if err := json.Unmarshal(b, &sStr); err != nil {
@ -57,10 +59,11 @@ func (s *State) UnmarshalJSON(b []byte) error {
return err
}
}
// TODO validate data (G1 & G2 subgroup checks, etc)
return err
}
// MarshalJSON implements the State json marshaler, compatible with the
// official Ethereum KZG Ceremony formats
func (s State) MarshalJSON() ([]byte, error) {
var sStr stateStr
sStr.ParticipantIDs = s.ParticipantIDs
@ -93,13 +96,14 @@ func (s State) MarshalJSON() ([]byte, error) {
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 {
var cStr batchContributionStr
if err := json.Unmarshal(b, &cStr); err != nil {
return err
}
var err error
g2 := bls12381.NewG2()
c.Contributions = make([]Contribution, len(cStr.Contributions))
for i := 0; i < len(cStr.Contributions); i++ {
@ -129,9 +133,10 @@ func (c *BatchContribution) UnmarshalJSON(b []byte) error {
return err
}
// MarshalJSON implements the BatchContribution json marshaler, compatible
// with the official Ethereum KZG Ceremony formats
func (c BatchContribution) MarshalJSON() ([]byte, error) {
var cStr batchContributionStr
g2 := bls12381.NewG2()
cStr.Contributions = make([]contributionStr, len(c.Contributions))
for i := 0; i < len(c.Contributions); i++ {
@ -143,7 +148,8 @@ func (c BatchContribution) MarshalJSON() ([]byte, error) {
cStr.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)
}
@ -184,7 +190,6 @@ type stateStr struct {
}
func g1PointsToStrings(points []*bls12381.PointG1) []string {
g1 := bls12381.NewG1() // TODO unify g1 instantiation (& g2)
n := len(points)
g1s := make([]string, n)
for i := 0; i < n; i++ {
@ -198,7 +203,6 @@ func g1PointsToStrings(points []*bls12381.PointG1) []string {
}
func g2PointsToStrings(points []*bls12381.PointG2) []string {
g2 := bls12381.NewG2()
n := len(points)
g2s := make([]string, n)
for i := 0; i < n; i++ {
@ -211,8 +215,10 @@ func g2PointsToStrings(points []*bls12381.PointG2) []string {
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) {
g1 := bls12381.NewG1() // TODO unify g1 instantiation (& g2)
n := len(s)
g1s := make([]*bls12381.PointG1, n)
for i := 0; i < n; i++ {
@ -227,12 +233,18 @@ func stringsToPointsG1(s []string) ([]*bls12381.PointG1, error) {
if err != nil {
return nil, err
}
if err := checkG1PointCorrectness(g1s_i); err != nil {
return nil, err
}
g1s[i] = g1s_i
}
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) {
g2 := bls12381.NewG2()
n := len(s)
g2s := make([]*bls12381.PointG2, n)
for i := 0; i < n; i++ {
@ -247,6 +259,9 @@ func stringsToPointsG2(s []string) ([]*bls12381.PointG2, error) {
if err != nil {
return nil, err
}
if err := checkG2PointCorrectness(g2s_i); err != nil {
return nil, err
}
g2s[i] = g2s_i
}
return g2s, nil

+ 4
- 0
parsers_test.go

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

+ 92
- 72
powersoftau.go

@ -7,39 +7,73 @@ import (
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
type Witness struct {
RunningProducts []*bls12381.PointG1
PotPubKeys []*bls12381.PointG2
BLSSignatures []*bls12381.PointG1
}
var g1 *bls12381.G1
var g2 *bls12381.G2
type Transcript struct {
NumG1Powers uint64
NumG2Powers uint64
PowersOfTau *SRS
Witness *Witness
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 BatchContribution struct {
Contributions []Contribution
type Transcript struct {
NumG1Powers uint64
NumG2Powers uint64
PowersOfTau *SRS
Witness *Witness
}
type Witness struct {
RunningProducts []*bls12381.PointG1
PotPubKeys []*bls12381.PointG2
BLSSignatures []*bls12381.PointG1
}
// SRS contains the powers of tau in G1 & G2, eg.
// [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁,
// [τ'⁰]₂, [τ'¹]₂, [τ'²]₂, ..., [τ'ⁿ⁻¹]₂
type SRS struct {
G1Powers []*bls12381.PointG1
G2Powers []*bls12381.PointG2
}
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) {
ns := State{}
ns.Transcripts = make([]Transcript, len(cs.Transcripts))
@ -66,6 +100,8 @@ func (cs *State) Contribute(randomness []byte) (*State, error) {
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) {
nb := BatchContribution{}
nb.Contributions = make([]Contribution, len(pb.Contributions))
@ -85,46 +121,21 @@ func (pb *BatchContribution) Contribute(randomness []byte) (*BatchContribution,
return &nb, nil
}
// SRS contains the powers of tau in G1 & G2, eg.
// [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁,
// [τ'⁰]₂, [τ'¹]₂, [τ'²]₂, ..., [τ'ⁿ⁻¹]₂
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
// newEmptySRS creates an empty SRS filled by [1]₁ & [1]₂ points in all
// respective arrays positions
func newEmptySRS(nG1, nG2 int) *SRS {
g1s := make([]*bls12381.PointG1, nG1)
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++ {
g1s[i] = g1.One()
// g1.MulScalar(g1s[i], one_G1, big.NewInt(int64(i)))
}
for i := 0; i < nG2; i++ {
g2s[i] = g2.One()
// g2.MulScalar(g2s[i], one_G2, big.NewInt(int64(i)))
}
return &SRS{g1s, g2s}
}
func tau(randomness []byte) *toxicWaste {
g2 := bls12381.NewG2()
tau := new(big.Int).Mod(
new(big.Int).SetBytes(randomness),
g2.Q())
@ -137,8 +148,6 @@ func tau(randomness []byte) *toxicWaste {
func computeContribution(t *toxicWaste, prevSRS *SRS) *SRS {
srs := newEmptySRS(len(prevSRS.G1Powers), len(prevSRS.G2Powers))
g1 := bls12381.NewG1()
g2 := bls12381.NewG2()
Q := g1.Q() // Q = |G1| == |G2|
// fmt.Println("Computing [τ'⁰]₁, [τ'¹]₁, [τ'²]₁, ..., [τ'ⁿ⁻¹]₁, for n =", len(prevSRS.G1s))
@ -158,7 +167,6 @@ func computeContribution(t *toxicWaste, prevSRS *SRS) *SRS {
}
func genProof(toxicWaste *toxicWaste, prevSRS, newSRS *SRS) *Proof {
g1 := bls12381.NewG1()
G1_p := g1.New()
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
@ -182,45 +190,57 @@ func Contribute(prevSRS *SRS, randomness []byte) (*SRS, *Proof, error) {
return newSRS, proof, nil
}
func checkG1PointCorrectness(p *bls12381.PointG1) error {
// i) non-empty
if p == nil {
return fmt.Errorf("empty point value")
}
// 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
}
// Verify checks the correct computation of the new SRS respectively from the
// previous SRS
func Verify(prevSRS, newSRS *SRS, proof *Proof) bool {
g1 := bls12381.NewG1()
g2 := bls12381.NewG2()
pairing := bls12381.NewEngine()
// 1. check that elements of the newSRS are valid points
for i := 0; i < len(newSRS.G1Powers); i++ {
// i) non-empty
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]) {
if err := checkG1PointCorrectness(newSRS.G1Powers[i]); err != nil {
return false
}
}
for i := 0; i < len(newSRS.G2Powers); i++ {
// i) non-empty
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]) {
if err := checkG2PointCorrectness(newSRS.G2Powers[i]); err != nil {
return false
}
}

+ 1
- 3
powersoftau_test.go

@ -2,7 +2,6 @@ package kzgceremony
import (
"encoding/json"
"fmt"
"io/ioutil"
"testing"
@ -58,7 +57,6 @@ func TestBatchContribution(t *testing.T) {
bc.Contribute([]byte("1111111111111111111111111111111111111111111111111111111111111111"))
c.Assert(err, qt.IsNil)
b, err := json.Marshal(nb)
_, err = json.Marshal(nb)
c.Assert(err, qt.IsNil)
fmt.Println(string(b))
}

Loading…
Cancel
Save