diff --git a/.gitignore b/.gitignore index 4006346..7788129 100644 --- a/.gitignore +++ b/.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 diff --git a/README.md b/README.md index 9eceb76..7d81a32 100644 --- a/README.md +++ b/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 1111111111111111111111111111111111111111111111111111111111111111 +``` +(where the 111...111 is your source of randomness) diff --git a/client/req.go b/client/req.go index 18f862e..44ec157 100644 --- a/client/req.go +++ b/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 { diff --git a/cmd/cmd.go b/cmd/cmd.go index d1b949c..28f244b 100644 --- a/cmd/cmd.go +++ b/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') diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..073ffc1 --- /dev/null +++ b/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 diff --git a/go.mod b/go.mod index 6b226d1..3aaedc3 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module kzgceremony +module github.com/arnaucube/eth-kzg-ceremony-alt go 1.19 diff --git a/parsers.go b/parsers.go index d0818bf..3893e4d 100644 --- a/parsers.go +++ b/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 diff --git a/parsers_test.go b/parsers_test.go index daa4b6c..b156281 100644 --- a/parsers_test.go +++ b/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) diff --git a/powersoftau.go b/powersoftau.go index 4829016..cb69260 100644 --- a/powersoftau.go +++ b/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 } } diff --git a/powersoftau_test.go b/powersoftau_test.go index 519ff5a..a9c92d4 100644 --- a/powersoftau_test.go +++ b/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)) }