From 8e1cc943f444e1880ded8cb1e054b25da6969cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Men=C3=A9ndez?= Date: Wed, 20 Nov 2024 12:26:23 +0100 Subject: [PATCH] feature: mimc bls12 377 (#31) add mimc hash implementation over bls12_377 ecc from gnark --- .github/workflows/circomtest.yml | 6 +-- .github/workflows/lint.yml | 27 ++++++---- .github/workflows/test.yml | 7 +-- .golangci.yml | 18 ------- dbg.go | 4 +- ff.go | 22 ++++++++ ff_test.go | 28 ++++++++++ go.mod | 5 ++ go.sum | 12 +++++ hash.go | 34 +++++++++++- hash_test.go | 14 +++++ helpers_test.go | 7 ++- .../go-data-generator/generator_test.go | 6 +-- tree.go | 50 +++++++++++------ vt.go | 54 +++++++++++++------ 15 files changed, 217 insertions(+), 77 deletions(-) delete mode 100644 .golangci.yml create mode 100644 ff.go create mode 100644 ff_test.go diff --git a/.github/workflows/circomtest.yml b/.github/workflows/circomtest.yml index 4d491f3..da43c3a 100644 --- a/.github/workflows/circomtest.yml +++ b/.github/workflows/circomtest.yml @@ -8,13 +8,13 @@ jobs: node-version: [16.x] go-version: [1.23.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: run circom tests diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index af3ab6a..7e3ef0d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,15 +2,24 @@ name: Lint on: [ push, pull_request ] jobs: lint: - runs-on: ubuntu-latest + strategy: + matrix: + go-version: [1.23.x] + platform: [ubuntu-latest] + runs-on: ${{ matrix.platform }} steps: + - name: Checkout code + uses: actions/checkout@v4 - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v5 with: - go-version: 1.23.x - - name: Checkout code - uses: actions/checkout@v2 - - name: Lint - run: | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.30.0 - $(go env GOPATH)/bin/golangci-lint run --timeout=5m -c .golangci.yml + go-version: ${{ matrix.go-version }} + cache: true + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + skip-cache: false + skip-pkg-cache: false + skip-build-cache: false + only-new-issues: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e83bc95..7ff3da6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,11 +9,12 @@ jobs: platform: [ubuntu-latest] runs-on: ${{ matrix.platform }} steps: + - name: Checkout code + uses: actions/checkout@v4 - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 + cache: true - name: Run tests run: go test ./... diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 08dd609..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,18 +0,0 @@ -issues: - max-same-issues: 0 - exclude-use-default: false -linters: - enable: - - whitespace - - gosec - - gci - - misspell - - gomnd - - gofmt - - goimports - - lll - - golint - - gocyclo -linters-settings: - lll: - line-length: 100 diff --git a/dbg.go b/dbg.go index 6db2cd2..22d0f18 100644 --- a/dbg.go +++ b/dbg.go @@ -28,7 +28,7 @@ func (d *dbgStats) incHash() { d.hash++ } -//nolint:unused +//nolint:all func (d *dbgStats) incDbGet() { if d == nil { return @@ -36,7 +36,7 @@ func (d *dbgStats) incDbGet() { d.dbGet++ } -//nolint:unused +//nolint:all func (d *dbgStats) incDbPut() { if d == nil { return diff --git a/ff.go b/ff.go new file mode 100644 index 0000000..444e985 --- /dev/null +++ b/ff.go @@ -0,0 +1,22 @@ +package arbo + +import "math/big" + +var ( + // BN254BaseField is the base field for the BN254 curve. + BN254BaseField, _ = new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10) + // BLS12377BaseField is the base field for the BLS12377 curve. + BLS12377BaseField, _ = new(big.Int).SetString("25825498262808887005865186224201665565126143020923472090132963926938185026661", 10) +) + +// BigToFF function returns the finite field representation of the big.Int +// provided. It uses the curve scalar field to represent the provided number. +func BigToFF(baseField, iv *big.Int) *big.Int { + z := big.NewInt(0) + if c := iv.Cmp(baseField); c == 0 { + return z + } else if c != 1 && iv.Cmp(z) != -1 { + return iv + } + return z.Mod(iv, baseField) +} diff --git a/ff_test.go b/ff_test.go new file mode 100644 index 0000000..9b0b05e --- /dev/null +++ b/ff_test.go @@ -0,0 +1,28 @@ +package arbo + +import ( + "math/big" + "testing" +) + +func TestBigToFF(t *testing.T) { + baseField := BN254BaseField + iv := new(big.Int).Sub(baseField, big.NewInt(1)) + // test with iv < baseField (the result should be iv) + z := BigToFF(baseField, iv) + if z.Cmp(iv) != 0 { + t.Fatalf("BigToFF failed: %v != %v", z, iv) + } + // test with iv > baseField (the result should be iv % baseField) + iv = new(big.Int).Add(baseField, big.NewInt(1)) + z = BigToFF(baseField, iv) + if z.Cmp(big.NewInt(1)) != 0 { + t.Fatalf("BigToFF failed: %v != 0", z) + } + // test with iv == baseField (the result should be 0) + iv = baseField + z = BigToFF(baseField, iv) + if z.Cmp(big.NewInt(0)) != 0 { + t.Fatalf("BigToFF failed: %v != 0", z) + } +} diff --git a/go.mod b/go.mod index 92a0dcc..62b5819 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( require ( github.com/DataDog/zstd v1.5.2 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.14.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect @@ -21,6 +22,8 @@ require ( github.com/cockroachdb/pebble v1.1.2 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.14.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect @@ -28,6 +31,7 @@ require ( github.com/klauspost/compress v1.17.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.19.1 // indirect @@ -39,4 +43,5 @@ require ( golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index c0c21c0..89b2d3d 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.14.2 h1:YXVoyPndbdvcEVcseEovVfp0qjJp7S+i5+xgp/Nfbdc= +github.com/bits-and-blooms/bitset v1.14.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= @@ -18,6 +20,10 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= +github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,6 +40,7 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/iden3/go-iden3-crypto v0.0.17 h1:NdkceRLJo/pI4UpcjVah4lN/a3yzxRUGXqxbWcYh9mY= github.com/iden3/go-iden3-crypto v0.0.17/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -46,6 +53,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -111,3 +121,5 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/hash.go b/hash.go index 047c254..3584d5f 100644 --- a/hash.go +++ b/hash.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "math/big" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/mimc" "github.com/iden3/go-iden3-crypto/poseidon" "golang.org/x/crypto/blake2b" ) @@ -16,6 +17,9 @@ var ( TypeHashPoseidon = []byte("poseidon") // TypeHashBlake2b represents the label for the HashFunction of Blake2b TypeHashBlake2b = []byte("blake2b") + // TypeHashMiMC_BLS12_377 represents the label for the HashFunction of MiMC + // over BLS12-377 curve + TypeHashMiMC_BLS12_377 = []byte("mimc_bls12_377") // HashFunctionSha256 contains the HashSha256 struct which implements // the HashFunction interface @@ -26,6 +30,9 @@ var ( // HashFunctionBlake2b contains the HashBlake2b struct which implements // the HashFunction interface HashFunctionBlake2b HashBlake2b + // HashFunctionMiMC_BLS12_377 contains the HashMiMC_BLS12_377 struct which + // implements the HashFunction interface + HashFunctionMiMC_BLS12_377 HashMiMC_BLS12_377 ) // Once Generics are at Go, this will be updated (August 2021 @@ -37,8 +44,6 @@ type HashFunction interface { Type() []byte Len() int Hash(...[]byte) ([]byte, error) - // CheckInput checks if the input is valid without computing the hash - // CheckInput(...[]byte) error } // HashSha256 implements the HashFunction interface for the Sha256 hash @@ -120,3 +125,28 @@ func (f HashBlake2b) Hash(b ...[]byte) ([]byte, error) { } return hasher.Sum(nil), nil } + +// HashMiMC_BLS12_377 implements the HashFunction interface for the MiMC hash +// over the BLS12-377 curve +type HashMiMC_BLS12_377 struct{} + +// Type returns the type of HashFunction for the HashMiMC_BLS12_377 +func (f HashMiMC_BLS12_377) Type() []byte { + return TypeHashMiMC_BLS12_377 +} + +// Len returns the length of the Hash output for the HashMiMC_BLS12_377 +func (f HashMiMC_BLS12_377) Len() int { + return mimc.BlockSize +} + +// Hash implements the hash method for the HashFunction HashMiMC_BLS12_377 +func (f HashMiMC_BLS12_377) Hash(b ...[]byte) ([]byte, error) { + h := mimc.NewMiMC() + for i := 0; i < len(b); i++ { + if _, err := h.Write(SwapEndianness(b[i])); err != nil { + return nil, err + } + } + return SwapEndianness(h.Sum(nil)), nil +} diff --git a/hash_test.go b/hash_test.go index da0d629..43aec20 100644 --- a/hash_test.go +++ b/hash_test.go @@ -53,3 +53,17 @@ func TestHashBlake2b(t *testing.T) { qt.Equals, "928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202") } + +func TestHashMiMC(t *testing.T) { + // MiMC hash + HashFunction := &HashMiMC_BLS12_377{} + b := []byte("test") + h, err := HashFunction.Hash(b) + if err != nil { + t.Fatal(err) + } + c := qt.New(t) + c.Assert(hex.EncodeToString(h), + qt.Equals, + "f881f34991492d823e02565c778b824bac5eacef6340b70ee90a8966a2e63900") +} diff --git a/helpers_test.go b/helpers_test.go index ad8f6e9..c66cbac 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -3,7 +3,6 @@ package arbo import ( "bytes" "io" - "io/ioutil" "os" "testing" "time" @@ -41,13 +40,13 @@ func checkRoots(c *qt.C, tree1, tree2 *Tree) { func storeTree(c *qt.C, tree *Tree, path string) { dump, err := tree.Dump(nil) c.Assert(err, qt.IsNil) - err = ioutil.WriteFile(path+"-"+time.Now().String()+".debug", dump, 0600) + err = os.WriteFile(path+"-"+time.Now().String()+".debug", dump, 0600) c.Assert(err, qt.IsNil) } // nolint:unused func readTree(c *qt.C, tree *Tree, path string) { - b, err := ioutil.ReadFile(path) //nolint:gosec + b, err := os.ReadFile(path) //nolint:gosec c.Assert(err, qt.IsNil) err = tree.ImportDump(b) c.Assert(err, qt.IsNil) @@ -102,7 +101,7 @@ func TestReadTreeDBG(t *testing.T) { // tree1 is generated by a loop of .Add path := "err-dump/tree1-2021-06-03 16:45:54.104449306 +0200 CEST m=+0.073874545.debug" - b, err := ioutil.ReadFile(path) + b, err := os.ReadFile(path) c.Assert(err, qt.IsNil) err = importDumpLoopAdd(tree1, b) c.Assert(err, qt.IsNil) diff --git a/testvectors/circom/go-data-generator/generator_test.go b/testvectors/circom/go-data-generator/generator_test.go index 8974add..80bd2d3 100644 --- a/testvectors/circom/go-data-generator/generator_test.go +++ b/testvectors/circom/go-data-generator/generator_test.go @@ -2,8 +2,8 @@ package main import ( "encoding/json" - "io/ioutil" "math/big" + "os" "testing" qt "github.com/frankban/quicktest" @@ -42,7 +42,7 @@ func TestGenerator(t *testing.T) { jCvp, err := json.Marshal(cvp) c.Assert(err, qt.IsNil) // store the data into a file that will be used at the circom test - err = ioutil.WriteFile("go-smt-verifier-inputs.json", jCvp, 0600) + err = os.WriteFile("go-smt-verifier-inputs.json", jCvp, 0600) c.Assert(err, qt.IsNil) // proof of non-existence @@ -52,6 +52,6 @@ func TestGenerator(t *testing.T) { jCvp, err = json.Marshal(cvp) c.Assert(err, qt.IsNil) // store the data into a file that will be used at the circom test - err = ioutil.WriteFile("go-smt-verifier-non-existence-inputs.json", jCvp, 0600) + err = os.WriteFile("go-smt-verifier-non-existence-inputs.json", jCvp, 0600) c.Assert(err, qt.IsNil) } diff --git a/tree.go b/tree.go index 54cd37a..ca36cd6 100644 --- a/tree.go +++ b/tree.go @@ -303,7 +303,7 @@ func (t *Tree) addBatchInDisk(wTx db.WriteTx, keys, values [][]byte) ([]Invalid, } if len(subRoots) != nCPU { - return nil, fmt.Errorf("This error should not be reached."+ + return nil, fmt.Errorf("this error should not be reached."+ " len(subRoots) != nCPU, len(subRoots)=%d, nCPU=%d."+ " Please report it in a new issue:"+ " https://github.com/vocdoni/arbo/issues/new", len(subRoots), nCPU) @@ -1463,9 +1463,11 @@ func (t *Tree) Graphviz(w io.Writer, fromRoot []byte) error { // generate a string Graphviz representation of the first NLevels of the tree // and writes it to w func (t *Tree) GraphvizFirstNLevels(w io.Writer, fromRoot []byte, untilLvl int) error { - fmt.Fprintf(w, `digraph hierarchy { + if _, err := fmt.Fprintf(w, `digraph hierarchy { node [fontname=Monospace,fontsize=10,shape=box] -`) +`); err != nil { + return err + } if fromRoot == nil { var err error fromRoot, err = t.RootWithTx(t.db) @@ -1482,14 +1484,20 @@ node [fontname=Monospace,fontsize=10,shape=box] switch v[0] { case PrefixValueEmpty: case PrefixValueLeaf: - fmt.Fprintf(w, "\"%v\" [style=filled];\n", hex.EncodeToString(k[:nChars])) + if _, err := fmt.Fprintf(w, "\"%v\" [style=filled];\n", hex.EncodeToString(k[:nChars])); err != nil { + return false + } // key & value from the leaf kB, vB := ReadLeafValue(v) - fmt.Fprintf(w, "\"%v\" -> {\"k:%v\\nv:%v\"}\n", + if _, err := fmt.Fprintf(w, "\"%v\" -> {\"k:%v\\nv:%v\"}\n", hex.EncodeToString(k[:nChars]), hex.EncodeToString(kB[:nChars]), - hex.EncodeToString(vB[:nChars])) - fmt.Fprintf(w, "\"k:%v\\nv:%v\" [style=dashed]\n", - hex.EncodeToString(kB[:nChars]), hex.EncodeToString(vB[:nChars])) + hex.EncodeToString(vB[:nChars])); err != nil { + return false + } + if _, err := fmt.Fprintf(w, "\"k:%v\\nv:%v\" [style=dashed]\n", + hex.EncodeToString(kB[:nChars]), hex.EncodeToString(vB[:nChars])); err != nil { + return false + } case PrefixValueIntermediate: l, r := ReadIntermediateChilds(v) lStr := hex.EncodeToString(l[:nChars]) @@ -1507,14 +1515,20 @@ node [fontname=Monospace,fontsize=10,shape=box] rStr) nEmpties++ } - fmt.Fprintf(w, "\"%v\" -> {\"%v\" \"%v\"}\n", hex.EncodeToString(k[:nChars]), - lStr, rStr) - fmt.Fprint(w, eStr) + if _, err := fmt.Fprintf(w, "\"%v\" -> {\"%v\" \"%v\"}\n", hex.EncodeToString(k[:nChars]), + lStr, rStr); err != nil { + return false + } + if _, err := fmt.Fprint(w, eStr); err != nil { + return false + } default: } return false }) - fmt.Fprintf(w, "}\n") + if _, err := fmt.Fprintf(w, "}\n"); err != nil { + return err + } return err } @@ -1540,15 +1554,19 @@ func (t *Tree) PrintGraphvizFirstNLevels(fromRoot []byte, untilLvl int) error { } } w := bytes.NewBufferString("") - fmt.Fprintf(w, - "--------\nGraphviz of the Tree with Root "+hex.EncodeToString(fromRoot)+":\n") + if _, err := fmt.Fprintf(w, + "--------\nGraphviz of the Tree with Root %s:\n", hex.EncodeToString(fromRoot)); err != nil { + return err + } err := t.GraphvizFirstNLevels(w, fromRoot, untilLvl) if err != nil { fmt.Println(w) return err } - fmt.Fprintf(w, - "End of Graphviz of the Tree with Root "+hex.EncodeToString(fromRoot)+"\n--------\n") + if _, err := fmt.Fprintf(w, + "End of Graphviz of the Tree with Root %s\n--------\n", hex.EncodeToString(fromRoot)); err != nil { + return err + } fmt.Println(w) return nil diff --git a/vt.go b/vt.go index 0fc65bd..4b0d503 100644 --- a/vt.go +++ b/vt.go @@ -171,7 +171,7 @@ func (t *vt) addBatch(ks, vs [][]byte) ([]Invalid, error) { } } if len(nodesAtL) != nCPU { - return nil, fmt.Errorf("This error should not be reached."+ + return nil, fmt.Errorf("this error should not be reached."+ " len(nodesAtL) != nCPU, len(nodesAtL)=%d, nCPU=%d."+ " Please report it in a new issue:"+ " https://github.com/vocdoni/arbo/issues/new", len(nodesAtL), nCPU) @@ -239,7 +239,7 @@ func (n *node) getNodesAtLevel(currLvl, l int) ([]*node, error) { return []*node{n}, nil } if currLvl >= l { - return nil, fmt.Errorf("This error should not be reached."+ + return nil, fmt.Errorf("this error should not be reached."+ " currLvl >= l, currLvl=%d, l=%d."+ " Please report it in a new issue:"+ " https://github.com/vocdoni/arbo/issues/new", currLvl, l) @@ -627,13 +627,17 @@ func (n *node) computeHashes(currLvl, maxLvl int, p *params, pairs [][2][]byte) //nolint:unused func (t *vt) graphviz(w io.Writer) error { - fmt.Fprintf(w, `digraph hierarchy { + if _, err := fmt.Fprintf(w, `digraph hierarchy { node [fontname=Monospace,fontsize=10,shape=box] -`) +`); err != nil { + return err + } if _, err := t.root.graphviz(w, t.params, 0); err != nil { return err } - fmt.Fprintf(w, "}\n") + if _, err := fmt.Fprintf(w, "}\n"); err != nil { + return err + } return nil } @@ -650,7 +654,9 @@ func (n *node) graphviz(w io.Writer, p *params, nEmpties int) (int, error) { if err != nil { return nEmpties, err } - fmt.Fprintf(w, "\"%p\" [style=filled,label=\"%v\"];\n", n, hex.EncodeToString(leafKey[:nChars])) + if _, err := fmt.Fprintf(w, "\"%p\" [style=filled,label=\"%v\"];\n", n, hex.EncodeToString(leafKey[:nChars])); err != nil { + return nEmpties, err + } k := n.k v := n.v @@ -661,14 +667,20 @@ func (n *node) graphviz(w io.Writer, p *params, nEmpties int) (int, error) { v = n.v[:nChars] } - fmt.Fprintf(w, "\"%p\" -> {\"k:%v\\nv:%v\"}\n", n, + if _, err := fmt.Fprintf(w, "\"%p\" -> {\"k:%v\\nv:%v\"}\n", n, hex.EncodeToString(k), - hex.EncodeToString(v)) - fmt.Fprintf(w, "\"k:%v\\nv:%v\" [style=dashed]\n", + hex.EncodeToString(v)); err != nil { + return nEmpties, err + } + if _, err := fmt.Fprintf(w, "\"k:%v\\nv:%v\" [style=dashed]\n", hex.EncodeToString(k), - hex.EncodeToString(v)) + hex.EncodeToString(v)); err != nil { + return nEmpties, err + } case vtMid: - fmt.Fprintf(w, "\"%p\" [label=\"\"];\n", n) + if _, err := fmt.Fprintf(w, "\"%p\" [label=\"\"];\n", n); err != nil { + return nEmpties, err + } lStr := fmt.Sprintf("%p", n.l) rStr := fmt.Sprintf("%p", n.r) @@ -685,8 +697,12 @@ func (n *node) graphviz(w io.Writer, p *params, nEmpties int) (int, error) { rStr) nEmpties++ } - fmt.Fprintf(w, "\"%p\" -> {\"%v\" \"%v\"}\n", n, lStr, rStr) - fmt.Fprint(w, eStr) + if _, err := fmt.Fprintf(w, "\"%p\" -> {\"%v\" \"%v\"}\n", n, lStr, rStr); err != nil { + return nEmpties, err + } + if _, err := fmt.Fprint(w, eStr); err != nil { + return nEmpties, err + } nEmpties, err := n.l.graphviz(w, p, nEmpties) if err != nil { return nEmpties, err @@ -707,15 +723,19 @@ func (n *node) graphviz(w io.Writer, p *params, nEmpties int) (int, error) { //nolint:unused func (t *vt) printGraphviz() error { w := bytes.NewBufferString("") - fmt.Fprintf(w, - "--------\nGraphviz:\n") + if _, err := fmt.Fprintf(w, + "--------\nGraphviz:\n"); err != nil { + return err + } err := t.graphviz(w) if err != nil { fmt.Println(w) return err } - fmt.Fprintf(w, - "End of Graphviz --------\n") + if _, err := fmt.Fprintf(w, + "End of Graphviz --------\n"); err != nil { + return err + } fmt.Println(w) return nil }