diff --git a/Makefile b/Makefile
index 5293d7c97c..a5975b5d39 100644
--- a/Makefile
+++ b/Makefile
@@ -53,7 +53,7 @@ GOLDFLAGS := $(GOLDFLAGS_BASE) \
UNIT_TEST_SOURCES := $(sort $(shell GO111MODULE=off go list ./... | grep -v /go-algorand/test/ ))
ALGOD_API_PACKAGES := $(sort $(shell GO111MODULE=off cd daemon/algod/api; go list ./... ))
-MSGP_GENERATE := ./protocol ./crypto ./data/basics ./data/transactions ./data/committee ./data/bookkeeping ./data/hashable ./auction ./agreement ./rpcs ./node ./ledger
+MSGP_GENERATE := ./protocol ./crypto ./crypto/compactcert ./data/basics ./data/transactions ./data/committee ./data/bookkeeping ./data/hashable ./auction ./agreement ./rpcs ./node ./ledger
default: build
diff --git a/crypto/compactcert/bigfloat.go b/crypto/compactcert/bigfloat.go
new file mode 100644
index 0000000000..a11aabf9de
--- /dev/null
+++ b/crypto/compactcert/bigfloat.go
@@ -0,0 +1,186 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "fmt"
+ "math/bits"
+)
+
+// A bigFloat represents the number mantissa*2^exp, which must be non-zero.
+//
+// A canonical representation is one where the highest bit of mantissa is
+// set. Every operation enforces canonicality of results.
+//
+// We use 32-bit values here to avoid requiring a 64bit-by-64bit-to-128bit
+// multiply operation for anyone that needs to implement this (even though
+// Go has this operation, as bits.Mul64).
+type bigFloat struct {
+ mantissa uint32
+ exp int32
+}
+
+// Each bigFloat is associated with a rounding mode (up, away from zero, or
+// down, towards zero). This is reflected by these two types of bigFloat.
+type bigFloatUp struct {
+ bigFloat
+}
+
+type bigFloatDn struct {
+ bigFloat
+}
+
+// canonicalize() ensures that the bigFloat is canonical.
+func (a *bigFloat) canonicalize() {
+ if a.mantissa == 0 {
+ // Just to avoid infinite loops in some error case.
+ return
+ }
+
+ for (a.mantissa & (1 << 31)) == 0 {
+ a.mantissa = a.mantissa << 1
+ a.exp = a.exp - 1
+ }
+}
+
+// doRoundUp adds one to the mantissa of a canonical bigFloat
+// to implement the rounding-up when there are leftover low bits.
+func (a *bigFloatUp) doRoundUp() {
+ if a.mantissa == (1<<32)-1 {
+ a.mantissa = 1 << 31
+ a.exp++
+ } else {
+ a.mantissa++
+ }
+}
+
+// geRaw returns whether a>=b. The Raw suffix indicates that
+// this comparison does not take rounding into account, and might
+// not be true if done with arbitrary-precision numbers.
+func (a *bigFloat) geRaw(b *bigFloat) bool {
+ if a.exp > b.exp {
+ return true
+ }
+
+ if a.exp < b.exp {
+ return false
+ }
+
+ return a.mantissa >= b.mantissa
+}
+
+// ge returns whether a>=b. It requires that a was computed with
+// rounding-down and b was computed with rounding-up, so that if
+// ge returns true, the arbitrary-precision computation would have
+// also been >=.
+func (a *bigFloatDn) ge(b *bigFloatUp) bool {
+ return a.geRaw(&b.bigFloat)
+}
+
+// setu64Dn sets the value to the supplied uint64 (which might get
+// rounded down in the process). x must not be zero. truncated
+// returns whether any non-zero bits were truncated (rounded down).
+func (a *bigFloat) setu64Dn(x uint64) (truncated bool, err error) {
+ if x == 0 {
+ return false, fmt.Errorf("bigFloat cannot be zero")
+ }
+
+ e := int32(0)
+
+ for x >= (1 << 32) {
+ if (x & 1) != 0 {
+ truncated = true
+ }
+
+ x = x >> 1
+ e = e + 1
+ }
+
+ a.mantissa = uint32(x)
+ a.exp = e
+ a.canonicalize()
+ return
+}
+
+// setu64 calls setu64Dn and implements rounding based on the type.
+func (a *bigFloatUp) setu64(x uint64) error {
+ truncated, err := a.setu64Dn(x)
+ if truncated {
+ a.doRoundUp()
+ }
+ return err
+}
+
+func (a *bigFloatDn) setu64(x uint64) error {
+ _, err := a.setu64Dn(x)
+ return err
+}
+
+// setu32 sets the value to the supplied uint32.
+func (a *bigFloat) setu32(x uint32) error {
+ if x == 0 {
+ return fmt.Errorf("bigFloat cannot be zero")
+ }
+
+ a.mantissa = x
+ a.exp = 0
+ a.canonicalize()
+ return nil
+}
+
+// setpow2 sets the value to 2^x.
+func (a *bigFloat) setpow2(x int32) {
+ a.mantissa = 1
+ a.exp = x
+ a.canonicalize()
+}
+
+// mulDn sets a to the product a*b, keeping the most significant 32 bits
+// of the product's mantissa. The return value indicates if any non-zero
+// bits were discarded (rounded down).
+func (a *bigFloat) mulDn(b *bigFloat) bool {
+ hi, lo := bits.Mul32(a.mantissa, b.mantissa)
+
+ a.mantissa = hi
+ a.exp = a.exp + b.exp + 32
+
+ if (a.mantissa & (1 << 31)) == 0 {
+ a.mantissa = (a.mantissa << 1) | (lo >> 31)
+ a.exp = a.exp - 1
+ lo = lo << 1
+ }
+
+ return lo != 0
+}
+
+// mul calls mulDn and implements appropriate rounding.
+// Types prevent multiplying two values with different rounding types.
+func (a *bigFloatUp) mul(b *bigFloatUp) {
+ truncated := a.mulDn(&b.bigFloat)
+ if truncated {
+ a.doRoundUp()
+ }
+}
+
+func (a *bigFloatDn) mul(b *bigFloatDn) {
+ a.mulDn(&b.bigFloat)
+}
+
+// String returns a string representation of a.
+func (a *bigFloat) String() string {
+ return fmt.Sprintf("%d*2^%d", a.mantissa, a.exp)
+}
diff --git a/crypto/compactcert/bigfloat_test.go b/crypto/compactcert/bigfloat_test.go
new file mode 100644
index 0000000000..c7c78e5749
--- /dev/null
+++ b/crypto/compactcert/bigfloat_test.go
@@ -0,0 +1,162 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/algorand/go-algorand/crypto"
+)
+
+func rand32() uint32 {
+ return uint32(crypto.RandUint64() & 0xffffffff)
+}
+
+func TestBigFloatRounding(t *testing.T) {
+ a := &bigFloatDn{}
+ b := &bigFloatUp{}
+
+ a.setu64(1 << 63)
+ b.setu64(1 << 63)
+
+ require.True(t, a.geRaw(&b.bigFloat))
+ require.True(t, b.geRaw(&a.bigFloat))
+
+ a.mul(a)
+ b.mul(b)
+
+ require.True(t, a.geRaw(&b.bigFloat))
+ require.True(t, b.geRaw(&a.bigFloat))
+
+ a.setu64((1 << 64) - 1)
+ b.setu64((1 << 64) - 1)
+
+ require.False(t, a.geRaw(&b.bigFloat))
+ require.True(t, b.geRaw(&a.bigFloat))
+
+ a.setu32((1 << 32) - 1)
+ b.setu32((1 << 32) - 1)
+
+ a.mul(a)
+ b.mul(b)
+
+ require.False(t, a.geRaw(&b.bigFloat))
+ require.True(t, b.geRaw(&a.bigFloat))
+}
+
+func TestBigFloat(t *testing.T) {
+ a := &bigFloatDn{}
+ b := &bigFloatDn{}
+
+ a.setu64(1)
+ require.Equal(t, a.mantissa, uint32(1<<31))
+ require.Equal(t, a.exp, int32(-31))
+
+ a.setu32(1)
+ require.Equal(t, a.mantissa, uint32(1<<31))
+ require.Equal(t, a.exp, int32(-31))
+
+ for i := int32(-256); i < 256; i++ {
+ a.setpow2(i)
+ require.Equal(t, a.mantissa, uint32(1<<31))
+ require.Equal(t, a.exp, i-31)
+ }
+
+ for i := 0; i < 8192; i++ {
+ x := rand32()
+ a.setu32(x)
+ require.True(t, a.exp <= 0)
+ require.Equal(t, x, a.mantissa>>(-a.exp))
+ }
+
+ for i := 0; i < 8192; i++ {
+ x := uint64(rand32())
+ a.setu64(x)
+ if a.exp <= 0 {
+ require.Equal(t, x, uint64(a.mantissa>>(-a.exp)))
+ }
+ if a.exp >= 0 {
+ require.Equal(t, x>>a.exp, uint64(a.mantissa))
+ }
+ }
+
+ for i := 0; i < 8192; i++ {
+ x := crypto.RandUint64()
+ a.setu64(x)
+ if a.exp <= 0 {
+ require.Equal(t, x, uint64(a.mantissa>>(-a.exp)))
+ }
+ if a.exp >= 0 {
+ require.Equal(t, x>>a.exp, uint64(a.mantissa))
+ }
+ }
+
+ for i := 0; i < 8192; i++ {
+ x := rand32()
+ y := rand32()
+ a.setu64(uint64(x))
+ b.setu64(uint64(y))
+
+ require.Equal(t, x >= y, a.geRaw(&b.bigFloat))
+ require.Equal(t, x < y, b.geRaw(&a.bigFloat))
+ require.True(t, a.geRaw(&a.bigFloat))
+ require.True(t, b.geRaw(&b.bigFloat))
+ }
+
+ xx := &big.Int{}
+ yy := &big.Int{}
+
+ for i := 0; i < 8192; i++ {
+ x := rand32()
+ y := rand32()
+ a.setu64(uint64(x))
+ b.setu64(uint64(y))
+ a.mul(b)
+
+ xx.SetUint64(uint64(x))
+ yy.SetUint64(uint64(y))
+ xx.Mul(xx, yy)
+ if a.exp > 0 {
+ xx.Rsh(xx, uint(a.exp))
+ }
+ if a.exp < 0 {
+ xx.Lsh(xx, uint(-a.exp))
+ }
+ require.Equal(t, a.mantissa, uint32(xx.Uint64()))
+ }
+}
+
+func BenchmarkBigFloatMulUp(b *testing.B) {
+ a := &bigFloatUp{}
+ a.setu32((1 << 32) - 1)
+
+ for i := 0; i < b.N; i++ {
+ a.mul(a)
+ }
+}
+
+func BenchmarkBigFloatMulDn(b *testing.B) {
+ a := &bigFloatDn{}
+ a.setu32((1 << 32) - 1)
+
+ for i := 0; i < b.N; i++ {
+ a.mul(a)
+ }
+}
diff --git a/crypto/compactcert/builder.go b/crypto/compactcert/builder.go
new file mode 100644
index 0000000000..a735627f4f
--- /dev/null
+++ b/crypto/compactcert/builder.go
@@ -0,0 +1,246 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "fmt"
+
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/crypto/merklearray"
+ "github.com/algorand/go-algorand/data/basics"
+)
+
+//msgp:ignore sigslot
+type sigslot struct {
+ // Weight is the weight of the participant signing this message.
+ // This information is tracked here for convenience, but it does
+ // not appear in the commitment to the sigs array; it comes from
+ // the Weight field of the corresponding participant.
+ Weight uint64
+
+ // Include the parts of the sigslot that form the commitment to
+ // the sigs array.
+ sigslotCommit
+}
+
+// Builder keeps track of signatures on a message and eventually produces
+// a compact certificate for that message.
+type Builder struct {
+ Params
+
+ sigs []sigslot // Indexed by pos in participants
+ sigsHasValidL bool // The L values in sigs are consistent with weights
+ signedWeight uint64 // Total weight of signatures so far
+ participants []Participant
+ parttree *merklearray.Tree
+
+ // Cached cert, if Build() was called and no subsequent
+ // Add() calls were made.
+ cert *Cert
+}
+
+// MkBuilder constructs an empty builder (with no signatures). The message
+// to be signed, as well as other security parameters, are specified in
+// param. The participants that will sign the message are in part and
+// parttree.
+func MkBuilder(param Params, part []Participant, parttree *merklearray.Tree) (*Builder, error) {
+ npart := len(part)
+
+ b := &Builder{
+ Params: param,
+ sigs: make([]sigslot, npart),
+ sigsHasValidL: false,
+ signedWeight: 0,
+ participants: part,
+ parttree: parttree,
+ }
+
+ return b, nil
+}
+
+// Present checks if the builder already contains a signature at a particular
+// offset.
+func (b *Builder) Present(pos uint64) bool {
+ return b.sigs[pos].Weight != 0
+}
+
+// Add a signature to the set of signatures available for building a certificate.
+// verifySig should be set to true in production; setting it to false is useful
+// for benchmarking to avoid the cost of signature checks.
+func (b *Builder) Add(pos uint64, sig crypto.OneTimeSignature, verifySig bool) error {
+ if b.Present(pos) {
+ return fmt.Errorf("position %d already added", pos)
+ }
+
+ // Check participants array
+ if pos >= uint64(len(b.participants)) {
+ return fmt.Errorf("pos %d >= len(participants) %d", pos, len(b.participants))
+ }
+
+ p := b.participants[pos]
+
+ if p.Weight == 0 {
+ return fmt.Errorf("position %d has zero weight", pos)
+ }
+
+ // Check signature
+ ephID := basics.OneTimeIDForRound(b.SigRound, p.KeyDilution)
+ if verifySig && !p.PK.Verify(ephID, b.Msg, sig) {
+ return fmt.Errorf("signature does not verify under ID %v", ephID)
+ }
+
+ // Remember the signature
+ b.sigs[pos].Weight = p.Weight
+ b.sigs[pos].Sig.OneTimeSignature = sig
+ b.signedWeight += p.Weight
+ b.cert = nil
+ b.sigsHasValidL = false
+ return nil
+}
+
+// Ready returns whether the certificate is ready to be built.
+func (b *Builder) Ready() bool {
+ return b.signedWeight > b.Params.ProvenWeight
+}
+
+// SignedWeight returns the total weight of signatures added so far.
+func (b *Builder) SignedWeight() uint64 {
+ return b.signedWeight
+}
+
+//msgp:ignore sigsToCommit
+type sigsToCommit []sigslot
+
+func (sc sigsToCommit) Length() uint64 {
+ return uint64(len(sc))
+}
+
+func (sc sigsToCommit) Get(pos uint64) (crypto.Hashable, error) {
+ if pos >= uint64(len(sc)) {
+ return nil, fmt.Errorf("pos %d past end %d", pos, len(sc))
+ }
+
+ return &sc[pos].sigslotCommit, nil
+}
+
+// coinIndex returns the position pos in the sigs array such that the sum
+// of all signature weights before pos is less than or equal to coinWeight,
+// but the sum of all signature weights up to and including pos exceeds
+// coinWeight.
+//
+// coinIndex works by doing a binary search on the sigs array.
+func (b *Builder) coinIndex(coinWeight uint64) (uint64, error) {
+ if !b.sigsHasValidL {
+ return 0, fmt.Errorf("coinIndex: need valid L values")
+ }
+
+ lo := uint64(0)
+ hi := uint64(len(b.sigs))
+
+again:
+ if lo >= hi {
+ return 0, fmt.Errorf("coinIndex: lo %d >= hi %d", lo, hi)
+ }
+
+ mid := (lo + hi) / 2
+ if coinWeight < b.sigs[mid].L {
+ hi = mid
+ goto again
+ }
+
+ if coinWeight < b.sigs[mid].L+b.sigs[mid].Weight {
+ return mid, nil
+ }
+
+ lo = mid + 1
+ goto again
+}
+
+// Build returns a compact certificate, if the builder has accumulated
+// enough signatures to construct it.
+func (b *Builder) Build() (*Cert, error) {
+ if b.cert != nil {
+ return b.cert, nil
+ }
+
+ if b.signedWeight <= b.Params.ProvenWeight {
+ return nil, fmt.Errorf("not enough signed weight: %d <= %d", b.signedWeight, b.Params.ProvenWeight)
+ }
+
+ // Commit to the sigs array
+ for i := 1; i < len(b.sigs); i++ {
+ b.sigs[i].L = b.sigs[i-1].L + b.sigs[i-1].Weight
+ }
+ b.sigsHasValidL = true
+
+ sigtree, err := merklearray.Build(sigsToCommit(b.sigs))
+ if err != nil {
+ return nil, err
+ }
+
+ // Reveal sufficient number of signatures
+ c := &Cert{
+ SigCommit: sigtree.Root(),
+ SignedWeight: b.signedWeight,
+ Reveals: make(map[uint64]Reveal),
+ }
+
+ nr, err := b.numReveals(b.signedWeight)
+ if err != nil {
+ return nil, err
+ }
+
+ var proofPositions []uint64
+
+ for j := uint64(0); j < nr; j++ {
+ coin := hashCoin(j, c.SigCommit, c.SignedWeight)
+ pos, err := b.coinIndex(coin)
+ if err != nil {
+ return nil, err
+ }
+
+ if pos >= uint64(len(b.participants)) {
+ return nil, fmt.Errorf("pos %d >= len(participants) %d", pos, len(b.participants))
+ }
+
+ // If we already revealed pos, no need to do it again
+ _, alreadyRevealed := c.Reveals[pos]
+ if alreadyRevealed {
+ continue
+ }
+
+ // Generate the reveal for pos
+ c.Reveals[pos] = Reveal{
+ SigSlot: b.sigs[pos].sigslotCommit,
+ Part: b.participants[pos],
+ }
+
+ proofPositions = append(proofPositions, pos)
+ }
+
+ c.SigProofs, err = sigtree.Prove(proofPositions)
+ if err != nil {
+ return nil, err
+ }
+
+ c.PartProofs, err = b.parttree.Prove(proofPositions)
+ if err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
diff --git a/crypto/compactcert/builder_test.go b/crypto/compactcert/builder_test.go
new file mode 100644
index 0000000000..82b01de827
--- /dev/null
+++ b/crypto/compactcert/builder_test.go
@@ -0,0 +1,239 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/crypto/merklearray"
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/protocol"
+
+ "github.com/stretchr/testify/require"
+)
+
+type TestMessage string
+
+func (m TestMessage) ToBeHashed() (protocol.HashID, []byte) {
+ return protocol.Message, []byte(m)
+}
+
+type PartCommit struct {
+ participants []Participant
+}
+
+func (pc PartCommit) Length() uint64 {
+ return uint64(len(pc.participants))
+}
+
+func (pc PartCommit) Get(pos uint64) (crypto.Hashable, error) {
+ if pos >= uint64(len(pc.participants)) {
+ return nil, fmt.Errorf("pos %d >= len %d", pos, len(pc.participants))
+ }
+
+ return pc.participants[pos], nil
+}
+
+func TestBuildVerify(t *testing.T) {
+ // Doing a full test of 1M accounts takes too much CPU time in CI.
+ doLargeTest := false
+
+ totalWeight := 10000000
+ npartHi := 10
+ npartLo := 9990
+
+ if doLargeTest {
+ npartHi *= 100
+ npartLo *= 100
+ }
+
+ npart := npartHi + npartLo
+
+ param := Params{
+ Msg: TestMessage("hello world"),
+ ProvenWeight: uint64(totalWeight / 2),
+ SigRound: 0,
+ SecKQ: 128,
+ }
+
+ // Share the key; we allow the same vote key to appear in multiple accounts..
+ key := crypto.GenerateOneTimeSignatureSecrets(0, 1)
+
+ var parts []Participant
+ var sigs []crypto.OneTimeSignature
+ for i := 0; i < npartHi; i++ {
+ part := Participant{
+ PK: key.OneTimeSignatureVerifier,
+ Weight: uint64(totalWeight / 2 / npartHi),
+ KeyDilution: 10000,
+ }
+
+ parts = append(parts, part)
+ }
+
+ for i := 0; i < npartLo; i++ {
+ part := Participant{
+ PK: key.OneTimeSignatureVerifier,
+ Weight: uint64(totalWeight / 2 / npartLo),
+ KeyDilution: 10000,
+ }
+
+ parts = append(parts, part)
+ }
+
+ ephID := basics.OneTimeIDForRound(0, parts[0].KeyDilution)
+ sig := key.Sign(ephID, param.Msg)
+
+ for i := 0; i < npart; i++ {
+ sigs = append(sigs, sig)
+ }
+
+ partcom, err := merklearray.Build(PartCommit{parts})
+ if err != nil {
+ t.Error(err)
+ }
+
+ b, err := MkBuilder(param, parts, partcom)
+ if err != nil {
+ t.Error(err)
+ }
+
+ for i := 0; i < npart; i++ {
+ err = b.Add(uint64(i), sigs[i], !doLargeTest)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ cert, err := b.Build()
+ if err != nil {
+ t.Error(err)
+ }
+
+ var someReveal Reveal
+ for _, rev := range cert.Reveals {
+ someReveal = rev
+ break
+ }
+
+ certenc := protocol.Encode(cert)
+ fmt.Printf("Cert size:\n")
+ fmt.Printf(" %6d elems sigproofs\n", len(cert.SigProofs))
+ fmt.Printf(" %6d bytes sigproofs\n", len(protocol.EncodeReflect(cert.SigProofs)))
+ fmt.Printf(" %6d bytes partproofs\n", len(protocol.EncodeReflect(cert.PartProofs)))
+ fmt.Printf(" %6d bytes sigproof per reveal\n", len(protocol.EncodeReflect(cert.SigProofs))/len(cert.Reveals))
+ fmt.Printf(" %6d reveals:\n", len(cert.Reveals))
+ fmt.Printf(" %6d bytes reveals[*] participant\n", len(protocol.Encode(&someReveal.Part)))
+ fmt.Printf(" %6d bytes reveals[*] sigslot\n", len(protocol.Encode(&someReveal.SigSlot)))
+ fmt.Printf(" %6d bytes reveals[*] total\n", len(protocol.Encode(&someReveal)))
+ fmt.Printf(" %6d bytes total\n", len(certenc))
+
+ verif := MkVerifier(param, partcom.Root())
+ err = verif.Verify(cert)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func BenchmarkBuildVerify(b *testing.B) {
+ totalWeight := 1000000
+ npart := 10000
+
+ param := Params{
+ Msg: TestMessage("hello world"),
+ ProvenWeight: uint64(totalWeight / 2),
+ SigRound: 0,
+ SecKQ: 128,
+ }
+
+ var parts []Participant
+ var partkeys []*crypto.OneTimeSignatureSecrets
+ var sigs []crypto.OneTimeSignature
+ for i := 0; i < npart; i++ {
+ key := crypto.GenerateOneTimeSignatureSecrets(0, 1)
+ part := Participant{
+ PK: key.OneTimeSignatureVerifier,
+ Weight: uint64(totalWeight / npart),
+ KeyDilution: 10000,
+ }
+
+ ephID := basics.OneTimeIDForRound(0, part.KeyDilution)
+ sig := key.Sign(ephID, param.Msg)
+
+ partkeys = append(partkeys, key)
+ sigs = append(sigs, sig)
+ parts = append(parts, part)
+ }
+
+ var cert *Cert
+ partcom, err := merklearray.Build(PartCommit{parts})
+ if err != nil {
+ b.Error(err)
+ }
+
+ b.Run("AddBuild", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ builder, err := MkBuilder(param, parts, partcom)
+ if err != nil {
+ b.Error(err)
+ }
+
+ for i := 0; i < npart; i++ {
+ err = builder.Add(uint64(i), sigs[i], true)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+
+ cert, err = builder.Build()
+ if err != nil {
+ b.Error(err)
+ }
+ }
+ })
+
+ b.Run("Verify", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ verif := MkVerifier(param, partcom.Root())
+ err = verif.Verify(cert)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+ })
+}
+
+func TestCoinIndex(t *testing.T) {
+ n := 1000
+ b := &Builder{
+ sigs: make([]sigslot, n),
+ sigsHasValidL: true,
+ }
+
+ for i := 0; i < n; i++ {
+ b.sigs[i].L = uint64(i)
+ b.sigs[i].Weight = 1
+ }
+
+ for i := 0; i < n; i++ {
+ pos, err := b.coinIndex(uint64(i))
+ require.NoError(t, err)
+ require.Equal(t, pos, uint64(i))
+ }
+}
diff --git a/crypto/compactcert/common.go b/crypto/compactcert/common.go
new file mode 100644
index 0000000000..604dbcf475
--- /dev/null
+++ b/crypto/compactcert/common.go
@@ -0,0 +1,103 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math/big"
+
+ "github.com/algorand/go-algorand/crypto"
+)
+
+// hashCoin returns a number in [0, signedWeight) with a nearly uniform
+// distribution, "randomized" by all of the supplied arguments.
+func hashCoin(j uint64, sigcom crypto.Digest, signedWeight uint64) uint64 {
+ hashinput := make([]byte, 16+crypto.DigestSize)
+ binary.LittleEndian.PutUint64(hashinput[0:], j)
+ binary.LittleEndian.PutUint64(hashinput[8:], signedWeight)
+ copy(hashinput[16:], sigcom[:])
+ h := crypto.Hash(hashinput)
+
+ i := &big.Int{}
+ i.SetBytes(h[:])
+
+ w := &big.Int{}
+ w.SetUint64(signedWeight)
+
+ res := &big.Int{}
+ res.Mod(i, w)
+ return res.Uint64()
+}
+
+// numReveals computes the number of reveals necessary to achieve the desired
+// security parameters. See section 8 of the ``Compact Certificates''
+// document for the analysis.
+//
+// numReveals is the smallest number that satisfies
+//
+// 2^-k >= 2^q * (provenWeight / signedWeight) ^ numReveals
+//
+// which is equivalent to the following:
+//
+// signedWeight ^ numReveals >= 2^(k+q) * provenWeight ^ numReveals
+//
+// To ensure that rounding errors do not reduce the security parameter,
+// we compute the left-hand side with rounding-down, and compute the
+// right-hand side with rounding-up.
+func numReveals(signedWeight uint64, provenWeight uint64, secKQ uint64, bound uint64) (uint64, error) {
+ n := uint64(0)
+
+ sw := &bigFloatDn{}
+ err := sw.setu64(signedWeight)
+ if err != nil {
+ return 0, err
+ }
+
+ pw := &bigFloatUp{}
+ err = pw.setu64(provenWeight)
+ if err != nil {
+ return 0, err
+ }
+
+ lhs := &bigFloatDn{}
+ err = lhs.setu64(1)
+ if err != nil {
+ return 0, err
+ }
+
+ rhs := &bigFloatUp{}
+ rhs.setpow2(int32(secKQ))
+
+ for {
+ if lhs.ge(rhs) {
+ return n, nil
+ }
+
+ if n >= bound {
+ return 0, fmt.Errorf("numReveals(%d, %d, %d) > %d", signedWeight, provenWeight, secKQ, bound)
+ }
+
+ lhs.mul(sw)
+ rhs.mul(pw)
+ n++
+ }
+}
+
+func (p Params) numReveals(signedWeight uint64) (uint64, error) {
+ return numReveals(signedWeight, p.ProvenWeight, p.SecKQ, maxReveals)
+}
diff --git a/crypto/compactcert/common_test.go b/crypto/compactcert/common_test.go
new file mode 100644
index 0000000000..aaefd10512
--- /dev/null
+++ b/crypto/compactcert/common_test.go
@@ -0,0 +1,99 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "testing"
+
+ "github.com/algorand/go-algorand/crypto"
+)
+
+func TestHashCoin(t *testing.T) {
+ var slots [32]uint64
+ var sigcom [32]byte
+
+ crypto.RandBytes(sigcom[:])
+
+ for j := uint64(0); j < 1000; j++ {
+ coin := hashCoin(j, sigcom, uint64(len(slots)))
+ if coin >= uint64(len(slots)) {
+ t.Errorf("hashCoin out of bounds")
+ }
+
+ slots[coin]++
+ }
+
+ for i, count := range slots {
+ if count < 3 {
+ t.Errorf("slot %d too low: %d", i, count)
+ }
+ if count > 100 {
+ t.Errorf("slot %d too high: %d", i, count)
+ }
+ }
+}
+
+func BenchmarkHashCoin(b *testing.B) {
+ var sigcom [32]byte
+ crypto.RandBytes(sigcom[:])
+
+ for i := 0; i < b.N; i++ {
+ hashCoin(uint64(i), sigcom, 1024)
+ }
+}
+
+func TestNumReveals(t *testing.T) {
+ billion := uint64(1000 * 1000 * 1000)
+ microalgo := uint64(1000 * 1000)
+ provenWeight := 2 * billion * microalgo
+ secKQ := uint64(128)
+ bound := uint64(1000)
+
+ for i := uint64(3); i < 10; i++ {
+ signedWeight := i * billion * microalgo
+ n, err := numReveals(signedWeight, provenWeight, secKQ, bound)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if n < 50 || n > 300 {
+ t.Errorf("numReveals(%d, %d, %d) = %d looks suspect",
+ signedWeight, provenWeight, secKQ, n)
+ }
+ }
+}
+
+func BenchmarkNumReveals(b *testing.B) {
+ billion := uint64(1000 * 1000 * 1000)
+ microalgo := uint64(1000 * 1000)
+ provenWeight := 100 * billion * microalgo
+ signedWeight := 110 * billion * microalgo
+ secKQ := uint64(128)
+ bound := uint64(1000)
+
+ nr, err := numReveals(signedWeight, provenWeight, secKQ, bound)
+ if nr < 900 {
+ b.Errorf("numReveals(%d, %d, %d) = %d < 900", signedWeight, provenWeight, secKQ, nr)
+ }
+
+ for i := 0; i < b.N; i++ {
+ _, err = numReveals(signedWeight, provenWeight, secKQ, bound)
+ if err != nil {
+ b.Error(err)
+ }
+ }
+}
diff --git a/crypto/compactcert/msgp_gen.go b/crypto/compactcert/msgp_gen.go
new file mode 100644
index 0000000000..4c12f3d9a9
--- /dev/null
+++ b/crypto/compactcert/msgp_gen.go
@@ -0,0 +1,1271 @@
+package compactcert
+
+// Code generated by github.com/algorand/msgp DO NOT EDIT.
+
+import (
+ "sort"
+
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/msgp/msgp"
+)
+
+// The following msgp objects are implemented in this file:
+// Cert
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+// CompactOneTimeSignature
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+// Participant
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+// Reveal
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+// sigslotCommit
+// |-----> (*) MarshalMsg
+// |-----> (*) CanMarshalMsg
+// |-----> (*) UnmarshalMsg
+// |-----> (*) CanUnmarshalMsg
+// |-----> (*) Msgsize
+// |-----> (*) MsgIsZero
+//
+
+// MarshalMsg implements msgp.Marshaler
+func (z *Cert) MarshalMsg(b []byte) (o []byte, err error) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0005Len := uint32(5)
+ var zb0005Mask uint8 /* 6 bits */
+ if len((*z).PartProofs) == 0 {
+ zb0005Len--
+ zb0005Mask |= 0x1
+ }
+ if len((*z).SigProofs) == 0 {
+ zb0005Len--
+ zb0005Mask |= 0x2
+ }
+ if (*z).SigCommit.MsgIsZero() {
+ zb0005Len--
+ zb0005Mask |= 0x8
+ }
+ if len((*z).Reveals) == 0 {
+ zb0005Len--
+ zb0005Mask |= 0x10
+ }
+ if (*z).SignedWeight == 0 {
+ zb0005Len--
+ zb0005Mask |= 0x20
+ }
+ // variable map header, size zb0005Len
+ o = append(o, 0x80|uint8(zb0005Len))
+ if zb0005Len != 0 {
+ if (zb0005Mask & 0x1) == 0 { // if not empty
+ // string "P"
+ o = append(o, 0xa1, 0x50)
+ if (*z).PartProofs == nil {
+ o = msgp.AppendNil(o)
+ } else {
+ o = msgp.AppendArrayHeader(o, uint32(len((*z).PartProofs)))
+ }
+ for zb0002 := range (*z).PartProofs {
+ o, err = (*z).PartProofs[zb0002].MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PartProofs", zb0002)
+ return
+ }
+ }
+ }
+ if (zb0005Mask & 0x2) == 0 { // if not empty
+ // string "S"
+ o = append(o, 0xa1, 0x53)
+ if (*z).SigProofs == nil {
+ o = msgp.AppendNil(o)
+ } else {
+ o = msgp.AppendArrayHeader(o, uint32(len((*z).SigProofs)))
+ }
+ for zb0001 := range (*z).SigProofs {
+ o, err = (*z).SigProofs[zb0001].MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "SigProofs", zb0001)
+ return
+ }
+ }
+ }
+ if (zb0005Mask & 0x8) == 0 { // if not empty
+ // string "c"
+ o = append(o, 0xa1, 0x63)
+ o, err = (*z).SigCommit.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "SigCommit")
+ return
+ }
+ }
+ if (zb0005Mask & 0x10) == 0 { // if not empty
+ // string "r"
+ o = append(o, 0xa1, 0x72)
+ if (*z).Reveals == nil {
+ o = msgp.AppendNil(o)
+ } else {
+ o = msgp.AppendMapHeader(o, uint32(len((*z).Reveals)))
+ }
+ zb0003_keys := make([]uint64, 0, len((*z).Reveals))
+ for zb0003 := range (*z).Reveals {
+ zb0003_keys = append(zb0003_keys, zb0003)
+ }
+ sort.Sort(SortUint64(zb0003_keys))
+ for _, zb0003 := range zb0003_keys {
+ zb0004 := (*z).Reveals[zb0003]
+ _ = zb0004
+ o = msgp.AppendUint64(o, zb0003)
+ o, err = zb0004.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "Reveals", zb0003)
+ return
+ }
+ }
+ }
+ if (zb0005Mask & 0x20) == 0 { // if not empty
+ // string "w"
+ o = append(o, 0xa1, 0x77)
+ o = msgp.AppendUint64(o, (*z).SignedWeight)
+ }
+ }
+ return
+}
+
+func (_ *Cert) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*Cert)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *Cert) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0005 int
+ var zb0006 bool
+ zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0005 > 0 {
+ zb0005--
+ bts, err = (*z).SigCommit.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigCommit")
+ return
+ }
+ }
+ if zb0005 > 0 {
+ zb0005--
+ (*z).SignedWeight, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SignedWeight")
+ return
+ }
+ }
+ if zb0005 > 0 {
+ zb0005--
+ var zb0007 int
+ var zb0008 bool
+ zb0007, zb0008, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigProofs")
+ return
+ }
+ if zb0007 > maxProofDigests {
+ err = msgp.ErrOverflow(uint64(zb0007), uint64(maxProofDigests))
+ err = msgp.WrapError(err, "struct-from-array", "SigProofs")
+ return
+ }
+ if zb0008 {
+ (*z).SigProofs = nil
+ } else if (*z).SigProofs != nil && cap((*z).SigProofs) >= zb0007 {
+ (*z).SigProofs = ((*z).SigProofs)[:zb0007]
+ } else {
+ (*z).SigProofs = make([]crypto.Digest, zb0007)
+ }
+ for zb0001 := range (*z).SigProofs {
+ bts, err = (*z).SigProofs[zb0001].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigProofs", zb0001)
+ return
+ }
+ }
+ }
+ if zb0005 > 0 {
+ zb0005--
+ var zb0009 int
+ var zb0010 bool
+ zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PartProofs")
+ return
+ }
+ if zb0009 > maxProofDigests {
+ err = msgp.ErrOverflow(uint64(zb0009), uint64(maxProofDigests))
+ err = msgp.WrapError(err, "struct-from-array", "PartProofs")
+ return
+ }
+ if zb0010 {
+ (*z).PartProofs = nil
+ } else if (*z).PartProofs != nil && cap((*z).PartProofs) >= zb0009 {
+ (*z).PartProofs = ((*z).PartProofs)[:zb0009]
+ } else {
+ (*z).PartProofs = make([]crypto.Digest, zb0009)
+ }
+ for zb0002 := range (*z).PartProofs {
+ bts, err = (*z).PartProofs[zb0002].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PartProofs", zb0002)
+ return
+ }
+ }
+ }
+ if zb0005 > 0 {
+ zb0005--
+ var zb0011 int
+ var zb0012 bool
+ zb0011, zb0012, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Reveals")
+ return
+ }
+ if zb0011 > maxReveals {
+ err = msgp.ErrOverflow(uint64(zb0011), uint64(maxReveals))
+ err = msgp.WrapError(err, "struct-from-array", "Reveals")
+ return
+ }
+ if zb0012 {
+ (*z).Reveals = nil
+ } else if (*z).Reveals == nil {
+ (*z).Reveals = make(map[uint64]Reveal, zb0011)
+ }
+ for zb0011 > 0 {
+ var zb0003 uint64
+ var zb0004 Reveal
+ zb0011--
+ zb0003, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Reveals")
+ return
+ }
+ bts, err = zb0004.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Reveals", zb0003)
+ return
+ }
+ (*z).Reveals[zb0003] = zb0004
+ }
+ }
+ if zb0005 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0005)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0006 {
+ (*z) = Cert{}
+ }
+ for zb0005 > 0 {
+ zb0005--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "c":
+ bts, err = (*z).SigCommit.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigCommit")
+ return
+ }
+ case "w":
+ (*z).SignedWeight, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SignedWeight")
+ return
+ }
+ case "S":
+ var zb0013 int
+ var zb0014 bool
+ zb0013, zb0014, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigProofs")
+ return
+ }
+ if zb0013 > maxProofDigests {
+ err = msgp.ErrOverflow(uint64(zb0013), uint64(maxProofDigests))
+ err = msgp.WrapError(err, "SigProofs")
+ return
+ }
+ if zb0014 {
+ (*z).SigProofs = nil
+ } else if (*z).SigProofs != nil && cap((*z).SigProofs) >= zb0013 {
+ (*z).SigProofs = ((*z).SigProofs)[:zb0013]
+ } else {
+ (*z).SigProofs = make([]crypto.Digest, zb0013)
+ }
+ for zb0001 := range (*z).SigProofs {
+ bts, err = (*z).SigProofs[zb0001].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigProofs", zb0001)
+ return
+ }
+ }
+ case "P":
+ var zb0015 int
+ var zb0016 bool
+ zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PartProofs")
+ return
+ }
+ if zb0015 > maxProofDigests {
+ err = msgp.ErrOverflow(uint64(zb0015), uint64(maxProofDigests))
+ err = msgp.WrapError(err, "PartProofs")
+ return
+ }
+ if zb0016 {
+ (*z).PartProofs = nil
+ } else if (*z).PartProofs != nil && cap((*z).PartProofs) >= zb0015 {
+ (*z).PartProofs = ((*z).PartProofs)[:zb0015]
+ } else {
+ (*z).PartProofs = make([]crypto.Digest, zb0015)
+ }
+ for zb0002 := range (*z).PartProofs {
+ bts, err = (*z).PartProofs[zb0002].UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PartProofs", zb0002)
+ return
+ }
+ }
+ case "r":
+ var zb0017 int
+ var zb0018 bool
+ zb0017, zb0018, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Reveals")
+ return
+ }
+ if zb0017 > maxReveals {
+ err = msgp.ErrOverflow(uint64(zb0017), uint64(maxReveals))
+ err = msgp.WrapError(err, "Reveals")
+ return
+ }
+ if zb0018 {
+ (*z).Reveals = nil
+ } else if (*z).Reveals == nil {
+ (*z).Reveals = make(map[uint64]Reveal, zb0017)
+ }
+ for zb0017 > 0 {
+ var zb0003 uint64
+ var zb0004 Reveal
+ zb0017--
+ zb0003, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Reveals")
+ return
+ }
+ bts, err = zb0004.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Reveals", zb0003)
+ return
+ }
+ (*z).Reveals[zb0003] = zb0004
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *Cert) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*Cert)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *Cert) Msgsize() (s int) {
+ s = 1 + 2 + (*z).SigCommit.Msgsize() + 2 + msgp.Uint64Size + 2 + msgp.ArrayHeaderSize
+ for zb0001 := range (*z).SigProofs {
+ s += (*z).SigProofs[zb0001].Msgsize()
+ }
+ s += 2 + msgp.ArrayHeaderSize
+ for zb0002 := range (*z).PartProofs {
+ s += (*z).PartProofs[zb0002].Msgsize()
+ }
+ s += 2 + msgp.MapHeaderSize
+ if (*z).Reveals != nil {
+ for zb0003, zb0004 := range (*z).Reveals {
+ _ = zb0003
+ _ = zb0004
+ s += 0 + msgp.Uint64Size + zb0004.Msgsize()
+ }
+ }
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *Cert) MsgIsZero() bool {
+ return ((*z).SigCommit.MsgIsZero()) && ((*z).SignedWeight == 0) && (len((*z).SigProofs) == 0) && (len((*z).PartProofs) == 0) && (len((*z).Reveals) == 0)
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z *CompactOneTimeSignature) MarshalMsg(b []byte) (o []byte, err error) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0001Len := uint32(6)
+ var zb0001Mask uint8 /* 8 bits */
+ if (*z).OneTimeSignature.PK.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x4
+ }
+ if (*z).OneTimeSignature.PK1Sig.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x8
+ }
+ if (*z).OneTimeSignature.PK2.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x10
+ }
+ if (*z).OneTimeSignature.PK2Sig.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x20
+ }
+ if (*z).OneTimeSignature.PKSigOld.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x40
+ }
+ if (*z).OneTimeSignature.Sig.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x80
+ }
+ // variable map header, size zb0001Len
+ o = append(o, 0x80|uint8(zb0001Len))
+ if zb0001Len != 0 {
+ if (zb0001Mask & 0x4) == 0 { // if not empty
+ // string "p"
+ o = append(o, 0xa1, 0x70)
+ o, err = (*z).OneTimeSignature.PK.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PK")
+ return
+ }
+ }
+ if (zb0001Mask & 0x8) == 0 { // if not empty
+ // string "p1s"
+ o = append(o, 0xa3, 0x70, 0x31, 0x73)
+ o, err = (*z).OneTimeSignature.PK1Sig.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PK1Sig")
+ return
+ }
+ }
+ if (zb0001Mask & 0x10) == 0 { // if not empty
+ // string "p2"
+ o = append(o, 0xa2, 0x70, 0x32)
+ o, err = (*z).OneTimeSignature.PK2.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PK2")
+ return
+ }
+ }
+ if (zb0001Mask & 0x20) == 0 { // if not empty
+ // string "p2s"
+ o = append(o, 0xa3, 0x70, 0x32, 0x73)
+ o, err = (*z).OneTimeSignature.PK2Sig.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PK2Sig")
+ return
+ }
+ }
+ if (zb0001Mask & 0x40) == 0 { // if not empty
+ // string "ps"
+ o = append(o, 0xa2, 0x70, 0x73)
+ o, err = (*z).OneTimeSignature.PKSigOld.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PKSigOld")
+ return
+ }
+ }
+ if (zb0001Mask & 0x80) == 0 { // if not empty
+ // string "s"
+ o = append(o, 0xa1, 0x73)
+ o, err = (*z).OneTimeSignature.Sig.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "Sig")
+ return
+ }
+ }
+ }
+ return
+}
+
+func (_ *CompactOneTimeSignature) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*CompactOneTimeSignature)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *CompactOneTimeSignature) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0001 int
+ var zb0002 bool
+ zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).OneTimeSignature.Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Sig")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).OneTimeSignature.PK.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PK")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).OneTimeSignature.PKSigOld.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PKSigOld")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).OneTimeSignature.PK2.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PK2")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).OneTimeSignature.PK1Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PK1Sig")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).OneTimeSignature.PK2Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PK2Sig")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0001)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0002 {
+ (*z) = CompactOneTimeSignature{}
+ }
+ for zb0001 > 0 {
+ zb0001--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "s":
+ bts, err = (*z).OneTimeSignature.Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Sig")
+ return
+ }
+ case "p":
+ bts, err = (*z).OneTimeSignature.PK.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PK")
+ return
+ }
+ case "ps":
+ bts, err = (*z).OneTimeSignature.PKSigOld.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PKSigOld")
+ return
+ }
+ case "p2":
+ bts, err = (*z).OneTimeSignature.PK2.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PK2")
+ return
+ }
+ case "p1s":
+ bts, err = (*z).OneTimeSignature.PK1Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PK1Sig")
+ return
+ }
+ case "p2s":
+ bts, err = (*z).OneTimeSignature.PK2Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PK2Sig")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *CompactOneTimeSignature) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*CompactOneTimeSignature)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *CompactOneTimeSignature) Msgsize() (s int) {
+ s = 1 + 2 + (*z).OneTimeSignature.Sig.Msgsize() + 2 + (*z).OneTimeSignature.PK.Msgsize() + 3 + (*z).OneTimeSignature.PKSigOld.Msgsize() + 3 + (*z).OneTimeSignature.PK2.Msgsize() + 4 + (*z).OneTimeSignature.PK1Sig.Msgsize() + 4 + (*z).OneTimeSignature.PK2Sig.Msgsize()
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *CompactOneTimeSignature) MsgIsZero() bool {
+ return ((*z).OneTimeSignature.Sig.MsgIsZero()) && ((*z).OneTimeSignature.PK.MsgIsZero()) && ((*z).OneTimeSignature.PKSigOld.MsgIsZero()) && ((*z).OneTimeSignature.PK2.MsgIsZero()) && ((*z).OneTimeSignature.PK1Sig.MsgIsZero()) && ((*z).OneTimeSignature.PK2Sig.MsgIsZero())
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z *Participant) MarshalMsg(b []byte) (o []byte, err error) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0001Len := uint32(3)
+ var zb0001Mask uint8 /* 4 bits */
+ if (*z).KeyDilution == 0 {
+ zb0001Len--
+ zb0001Mask |= 0x2
+ }
+ if (*z).PK.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x4
+ }
+ if (*z).Weight == 0 {
+ zb0001Len--
+ zb0001Mask |= 0x8
+ }
+ // variable map header, size zb0001Len
+ o = append(o, 0x80|uint8(zb0001Len))
+ if zb0001Len != 0 {
+ if (zb0001Mask & 0x2) == 0 { // if not empty
+ // string "d"
+ o = append(o, 0xa1, 0x64)
+ o = msgp.AppendUint64(o, (*z).KeyDilution)
+ }
+ if (zb0001Mask & 0x4) == 0 { // if not empty
+ // string "p"
+ o = append(o, 0xa1, 0x70)
+ o, err = (*z).PK.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "PK")
+ return
+ }
+ }
+ if (zb0001Mask & 0x8) == 0 { // if not empty
+ // string "w"
+ o = append(o, 0xa1, 0x77)
+ o = msgp.AppendUint64(o, (*z).Weight)
+ }
+ }
+ return
+}
+
+func (_ *Participant) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*Participant)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *Participant) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0001 int
+ var zb0002 bool
+ zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).PK.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "PK")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ (*z).Weight, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Weight")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ (*z).KeyDilution, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "KeyDilution")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0001)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0002 {
+ (*z) = Participant{}
+ }
+ for zb0001 > 0 {
+ zb0001--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "p":
+ bts, err = (*z).PK.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "PK")
+ return
+ }
+ case "w":
+ (*z).Weight, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Weight")
+ return
+ }
+ case "d":
+ (*z).KeyDilution, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "KeyDilution")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *Participant) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*Participant)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *Participant) Msgsize() (s int) {
+ s = 1 + 2 + (*z).PK.Msgsize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *Participant) MsgIsZero() bool {
+ return ((*z).PK.MsgIsZero()) && ((*z).Weight == 0) && ((*z).KeyDilution == 0)
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z *Reveal) MarshalMsg(b []byte) (o []byte, err error) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0001Len := uint32(2)
+ var zb0001Mask uint8 /* 3 bits */
+ if (*z).Part.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x2
+ }
+ if ((*z).SigSlot.Sig.MsgIsZero()) && ((*z).SigSlot.L == 0) {
+ zb0001Len--
+ zb0001Mask |= 0x4
+ }
+ // variable map header, size zb0001Len
+ o = append(o, 0x80|uint8(zb0001Len))
+ if zb0001Len != 0 {
+ if (zb0001Mask & 0x2) == 0 { // if not empty
+ // string "p"
+ o = append(o, 0xa1, 0x70)
+ o, err = (*z).Part.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "Part")
+ return
+ }
+ }
+ if (zb0001Mask & 0x4) == 0 { // if not empty
+ // string "s"
+ o = append(o, 0xa1, 0x73)
+ // omitempty: check for empty values
+ zb0002Len := uint32(2)
+ var zb0002Mask uint8 /* 3 bits */
+ if (*z).SigSlot.L == 0 {
+ zb0002Len--
+ zb0002Mask |= 0x2
+ }
+ if (*z).SigSlot.Sig.MsgIsZero() {
+ zb0002Len--
+ zb0002Mask |= 0x4
+ }
+ // variable map header, size zb0002Len
+ o = append(o, 0x80|uint8(zb0002Len))
+ if (zb0002Mask & 0x2) == 0 { // if not empty
+ // string "l"
+ o = append(o, 0xa1, 0x6c)
+ o = msgp.AppendUint64(o, (*z).SigSlot.L)
+ }
+ if (zb0002Mask & 0x4) == 0 { // if not empty
+ // string "s"
+ o = append(o, 0xa1, 0x73)
+ o, err = (*z).SigSlot.Sig.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot", "Sig")
+ return
+ }
+ }
+ }
+ }
+ return
+}
+
+func (_ *Reveal) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*Reveal)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *Reveal) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0001 int
+ var zb0002 bool
+ zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0001 > 0 {
+ zb0001--
+ var zb0003 int
+ var zb0004 bool
+ zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0003, zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot")
+ return
+ }
+ if zb0003 > 0 {
+ zb0003--
+ bts, err = (*z).SigSlot.Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot", "struct-from-array", "Sig")
+ return
+ }
+ }
+ if zb0003 > 0 {
+ zb0003--
+ (*z).SigSlot.L, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot", "struct-from-array", "L")
+ return
+ }
+ }
+ if zb0003 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0003)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot", "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot")
+ return
+ }
+ if zb0004 {
+ (*z).SigSlot = sigslotCommit{}
+ }
+ for zb0003 > 0 {
+ zb0003--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot")
+ return
+ }
+ switch string(field) {
+ case "s":
+ bts, err = (*z).SigSlot.Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot", "Sig")
+ return
+ }
+ case "l":
+ (*z).SigSlot.L, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot", "L")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "SigSlot")
+ return
+ }
+ }
+ }
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).Part.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Part")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0001)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0002 {
+ (*z) = Reveal{}
+ }
+ for zb0001 > 0 {
+ zb0001--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "s":
+ var zb0005 int
+ var zb0006 bool
+ zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot")
+ return
+ }
+ if zb0005 > 0 {
+ zb0005--
+ bts, err = (*z).SigSlot.Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot", "struct-from-array", "Sig")
+ return
+ }
+ }
+ if zb0005 > 0 {
+ zb0005--
+ (*z).SigSlot.L, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot", "struct-from-array", "L")
+ return
+ }
+ }
+ if zb0005 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0005)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot", "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot")
+ return
+ }
+ if zb0006 {
+ (*z).SigSlot = sigslotCommit{}
+ }
+ for zb0005 > 0 {
+ zb0005--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot")
+ return
+ }
+ switch string(field) {
+ case "s":
+ bts, err = (*z).SigSlot.Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot", "Sig")
+ return
+ }
+ case "l":
+ (*z).SigSlot.L, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot", "L")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err, "SigSlot")
+ return
+ }
+ }
+ }
+ }
+ case "p":
+ bts, err = (*z).Part.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Part")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *Reveal) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*Reveal)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *Reveal) Msgsize() (s int) {
+ s = 1 + 2 + 1 + 2 + (*z).SigSlot.Sig.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).Part.Msgsize()
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *Reveal) MsgIsZero() bool {
+ return (((*z).SigSlot.Sig.MsgIsZero()) && ((*z).SigSlot.L == 0)) && ((*z).Part.MsgIsZero())
+}
+
+// MarshalMsg implements msgp.Marshaler
+func (z *sigslotCommit) MarshalMsg(b []byte) (o []byte, err error) {
+ o = msgp.Require(b, z.Msgsize())
+ // omitempty: check for empty values
+ zb0001Len := uint32(2)
+ var zb0001Mask uint8 /* 3 bits */
+ if (*z).L == 0 {
+ zb0001Len--
+ zb0001Mask |= 0x2
+ }
+ if (*z).Sig.MsgIsZero() {
+ zb0001Len--
+ zb0001Mask |= 0x4
+ }
+ // variable map header, size zb0001Len
+ o = append(o, 0x80|uint8(zb0001Len))
+ if zb0001Len != 0 {
+ if (zb0001Mask & 0x2) == 0 { // if not empty
+ // string "l"
+ o = append(o, 0xa1, 0x6c)
+ o = msgp.AppendUint64(o, (*z).L)
+ }
+ if (zb0001Mask & 0x4) == 0 { // if not empty
+ // string "s"
+ o = append(o, 0xa1, 0x73)
+ o, err = (*z).Sig.MarshalMsg(o)
+ if err != nil {
+ err = msgp.WrapError(err, "Sig")
+ return
+ }
+ }
+ }
+ return
+}
+
+func (_ *sigslotCommit) CanMarshalMsg(z interface{}) bool {
+ _, ok := (z).(*sigslotCommit)
+ return ok
+}
+
+// UnmarshalMsg implements msgp.Unmarshaler
+func (z *sigslotCommit) UnmarshalMsg(bts []byte) (o []byte, err error) {
+ var field []byte
+ _ = field
+ var zb0001 int
+ var zb0002 bool
+ zb0001, zb0002, bts, err = msgp.ReadMapHeaderBytes(bts)
+ if _, ok := err.(msgp.TypeError); ok {
+ zb0001, zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0001 > 0 {
+ zb0001--
+ bts, err = (*z).Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "Sig")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ zb0001--
+ (*z).L, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array", "L")
+ return
+ }
+ }
+ if zb0001 > 0 {
+ err = msgp.ErrTooManyArrayFields(zb0001)
+ if err != nil {
+ err = msgp.WrapError(err, "struct-from-array")
+ return
+ }
+ }
+ } else {
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ if zb0002 {
+ (*z) = sigslotCommit{}
+ }
+ for zb0001 > 0 {
+ zb0001--
+ field, bts, err = msgp.ReadMapKeyZC(bts)
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ switch string(field) {
+ case "s":
+ bts, err = (*z).Sig.UnmarshalMsg(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "Sig")
+ return
+ }
+ case "l":
+ (*z).L, bts, err = msgp.ReadUint64Bytes(bts)
+ if err != nil {
+ err = msgp.WrapError(err, "L")
+ return
+ }
+ default:
+ err = msgp.ErrNoField(string(field))
+ if err != nil {
+ err = msgp.WrapError(err)
+ return
+ }
+ }
+ }
+ }
+ o = bts
+ return
+}
+
+func (_ *sigslotCommit) CanUnmarshalMsg(z interface{}) bool {
+ _, ok := (z).(*sigslotCommit)
+ return ok
+}
+
+// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
+func (z *sigslotCommit) Msgsize() (s int) {
+ s = 1 + 2 + (*z).Sig.Msgsize() + 2 + msgp.Uint64Size
+ return
+}
+
+// MsgIsZero returns whether this is a zero value
+func (z *sigslotCommit) MsgIsZero() bool {
+ return ((*z).Sig.MsgIsZero()) && ((*z).L == 0)
+}
diff --git a/crypto/compactcert/msgp_gen_test.go b/crypto/compactcert/msgp_gen_test.go
new file mode 100644
index 0000000000..28f0e9d163
--- /dev/null
+++ b/crypto/compactcert/msgp_gen_test.go
@@ -0,0 +1,322 @@
+// +build !skip_msgp_testing
+
+package compactcert
+
+// Code generated by github.com/algorand/msgp DO NOT EDIT.
+
+import (
+ "testing"
+
+ "github.com/algorand/go-algorand/protocol"
+ "github.com/algorand/msgp/msgp"
+)
+
+func TestMarshalUnmarshalCert(t *testing.T) {
+ v := Cert{}
+ bts, err := v.MarshalMsg(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingCert(t *testing.T) {
+ protocol.RunEncodingTest(t, &Cert{})
+}
+
+func BenchmarkMarshalMsgCert(b *testing.B) {
+ v := Cert{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgCert(b *testing.B) {
+ v := Cert{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts, _ = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts, _ = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalCert(b *testing.B) {
+ v := Cert{}
+ bts, _ := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalUnmarshalCompactOneTimeSignature(t *testing.T) {
+ v := CompactOneTimeSignature{}
+ bts, err := v.MarshalMsg(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingCompactOneTimeSignature(t *testing.T) {
+ protocol.RunEncodingTest(t, &CompactOneTimeSignature{})
+}
+
+func BenchmarkMarshalMsgCompactOneTimeSignature(b *testing.B) {
+ v := CompactOneTimeSignature{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgCompactOneTimeSignature(b *testing.B) {
+ v := CompactOneTimeSignature{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts, _ = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts, _ = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalCompactOneTimeSignature(b *testing.B) {
+ v := CompactOneTimeSignature{}
+ bts, _ := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalUnmarshalParticipant(t *testing.T) {
+ v := Participant{}
+ bts, err := v.MarshalMsg(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingParticipant(t *testing.T) {
+ protocol.RunEncodingTest(t, &Participant{})
+}
+
+func BenchmarkMarshalMsgParticipant(b *testing.B) {
+ v := Participant{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgParticipant(b *testing.B) {
+ v := Participant{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts, _ = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts, _ = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalParticipant(b *testing.B) {
+ v := Participant{}
+ bts, _ := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalUnmarshalReveal(t *testing.T) {
+ v := Reveal{}
+ bts, err := v.MarshalMsg(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingReveal(t *testing.T) {
+ protocol.RunEncodingTest(t, &Reveal{})
+}
+
+func BenchmarkMarshalMsgReveal(b *testing.B) {
+ v := Reveal{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgReveal(b *testing.B) {
+ v := Reveal{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts, _ = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts, _ = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalReveal(b *testing.B) {
+ v := Reveal{}
+ bts, _ := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func TestMarshalUnmarshalsigslotCommit(t *testing.T) {
+ v := sigslotCommit{}
+ bts, err := v.MarshalMsg(nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ left, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
+ }
+
+ left, err = msgp.Skip(bts)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(left) > 0 {
+ t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
+ }
+}
+
+func TestRandomizedEncodingsigslotCommit(t *testing.T) {
+ protocol.RunEncodingTest(t, &sigslotCommit{})
+}
+
+func BenchmarkMarshalMsgsigslotCommit(b *testing.B) {
+ v := sigslotCommit{}
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.MarshalMsg(nil)
+ }
+}
+
+func BenchmarkAppendMsgsigslotCommit(b *testing.B) {
+ v := sigslotCommit{}
+ bts := make([]byte, 0, v.Msgsize())
+ bts, _ = v.MarshalMsg(bts[0:0])
+ b.SetBytes(int64(len(bts)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bts, _ = v.MarshalMsg(bts[0:0])
+ }
+}
+
+func BenchmarkUnmarshalsigslotCommit(b *testing.B) {
+ v := sigslotCommit{}
+ bts, _ := v.MarshalMsg(nil)
+ b.ReportAllocs()
+ b.SetBytes(int64(len(bts)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := v.UnmarshalMsg(bts)
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/crypto/compactcert/structs.go b/crypto/compactcert/structs.go
new file mode 100644
index 0000000000..37f756073b
--- /dev/null
+++ b/crypto/compactcert/structs.go
@@ -0,0 +1,115 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/data/basics"
+ "github.com/algorand/go-algorand/protocol"
+)
+
+// Params defines common parameters for the verifier and builder.
+type Params struct {
+ Msg crypto.Hashable // Message to be cerified
+ ProvenWeight uint64 // Weight threshold proven by the certificate
+ SigRound basics.Round // Ephemeral signature round to expect
+ SecKQ uint64 // Security parameter (k+q) from analysis document
+}
+
+// A Participant corresponds to an account whose AccountData.Status
+// is Online, and for which the expected sigRound satisfies
+// AccountData.VoteFirstValid <= sigRound <= AccountData.VoteLastValid.
+//
+// In the Algorand ledger, it is possible for multiple accounts to have
+// the same PK. Thus, the PK is not necessarily unique among Participants.
+// However, each account will produce a unique Participant struct, to avoid
+// potential DoS attacks where one account claims to have the same VoteID PK
+// as another account.
+type Participant struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ // PK is AccountData.VoteID.
+ PK crypto.OneTimeSignatureVerifier `codec:"p"`
+
+ // Weight is AccountData.MicroAlgos.
+ Weight uint64 `codec:"w"`
+
+ // KeyDilution is AccountData.KeyDilution() with the protocol for sigRound
+ // as expected by the Builder.
+ KeyDilution uint64 `codec:"d"`
+}
+
+// ToBeHashed implements the crypto.Hashable interface.
+func (p Participant) ToBeHashed() (protocol.HashID, []byte) {
+ return protocol.CompactCertPart, protocol.Encode(&p)
+}
+
+// CompactOneTimeSignature is crypto.OneTimeSignature with omitempty
+type CompactOneTimeSignature struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+ crypto.OneTimeSignature
+}
+
+// A sigslotCommit is a single slot in the sigs array that forms the certificate.
+type sigslotCommit struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ // Sig is a signature by the participant on the expected message.
+ Sig CompactOneTimeSignature `codec:"s"`
+
+ // L is the total weight of signatures in lower-numbered slots.
+ // This is initialized once the builder has collected a sufficient
+ // number of signatures.
+ L uint64 `codec:"l"`
+}
+
+func (ssc sigslotCommit) ToBeHashed() (protocol.HashID, []byte) {
+ return protocol.CompactCertSig, protocol.Encode(&ssc)
+}
+
+// Reveal is a single array position revealed as part of a compact
+// certificate. It reveals an element of the signature array and
+// the corresponding element of the participants array.
+type Reveal struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ SigSlot sigslotCommit `codec:"s"`
+ Part Participant `codec:"p"`
+}
+
+// maxReveals is a bound on allocation and on numReveals to limit log computation
+const maxReveals = 1024
+const maxProofDigests = 20 * maxReveals
+
+// Cert represents a compact certificate.
+type Cert struct {
+ _struct struct{} `codec:",omitempty,omitemptyarray"`
+
+ SigCommit crypto.Digest `codec:"c"`
+ SignedWeight uint64 `codec:"w"`
+ SigProofs []crypto.Digest `codec:"S,allocbound=maxProofDigests"`
+ PartProofs []crypto.Digest `codec:"P,allocbound=maxProofDigests"`
+
+ // Reveals is a sparse map from the position being revealed
+ // to the corresponding elements from the sigs and participants
+ // arrays.
+ Reveals map[uint64]Reveal `codec:"r,allocbound=maxReveals"`
+}
+
+// SortUint64 implements sorting by uint64 keys for
+// canonical encoding of maps in msgpack format.
+type SortUint64 = basics.SortUint64
diff --git a/crypto/compactcert/verifier.go b/crypto/compactcert/verifier.go
new file mode 100644
index 0000000000..cfb7a7f792
--- /dev/null
+++ b/crypto/compactcert/verifier.go
@@ -0,0 +1,96 @@
+// Copyright (C) 2019-2020 Algorand, Inc.
+// This file is part of go-algorand
+//
+// go-algorand is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// go-algorand is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with go-algorand. If not, see .
+
+package compactcert
+
+import (
+ "fmt"
+
+ "github.com/algorand/go-algorand/crypto"
+ "github.com/algorand/go-algorand/crypto/merklearray"
+ "github.com/algorand/go-algorand/data/basics"
+)
+
+// Verifier is used to verify a compact certificate.
+type Verifier struct {
+ Params
+
+ partcom crypto.Digest
+}
+
+// MkVerifier constructs a verifier to check the compact certificate
+// on the message specified in p, with partcom specifying the Merkle
+// root of the participants that must sign the message.
+func MkVerifier(p Params, partcom crypto.Digest) *Verifier {
+ return &Verifier{
+ Params: p,
+ partcom: partcom,
+ }
+}
+
+// Verify checks if c is a valid compact certificate for the message
+// and participants that were used to construct the Verifier.
+func (v *Verifier) Verify(c *Cert) error {
+ if c.SignedWeight <= v.ProvenWeight {
+ return fmt.Errorf("cert signed weight %d <= proven weight %d", c.SignedWeight, v.ProvenWeight)
+ }
+
+ // Verify all of the reveals
+ sigs := make(map[uint64]crypto.Hashable)
+ parts := make(map[uint64]crypto.Hashable)
+ for pos, r := range c.Reveals {
+ sigs[pos] = r.SigSlot
+ parts[pos] = r.Part
+
+ ephID := basics.OneTimeIDForRound(v.SigRound, r.Part.KeyDilution)
+ if !r.Part.PK.Verify(ephID, v.Msg, r.SigSlot.Sig.OneTimeSignature) {
+ return fmt.Errorf("signature in reveal pos %d does not verify", pos)
+ }
+ }
+
+ err := merklearray.Verify(c.SigCommit, sigs, c.SigProofs)
+ if err != nil {
+ return err
+ }
+
+ err = merklearray.Verify(v.partcom, parts, c.PartProofs)
+ if err != nil {
+ return err
+ }
+
+ // Verify that the reveals contain the right coins
+ nr, err := v.numReveals(c.SignedWeight)
+ if err != nil {
+ return err
+ }
+
+ for j := uint64(0); j < nr; j++ {
+ coin := hashCoin(j, c.SigCommit, c.SignedWeight)
+ matchingReveal := false
+ for _, r := range c.Reveals {
+ if r.SigSlot.L <= coin && coin < r.SigSlot.L+r.Part.Weight {
+ matchingReveal = true
+ break
+ }
+ }
+
+ if !matchingReveal {
+ return fmt.Errorf("no reveal for coin %d at %d", j, coin)
+ }
+ }
+
+ return nil
+}
diff --git a/protocol/hash.go b/protocol/hash.go
index d3f821690e..4ab2786e69 100644
--- a/protocol/hash.go
+++ b/protocol/hash.go
@@ -28,6 +28,9 @@ const (
AuctionParams HashID = "aP"
AuctionSettlement HashID = "aS"
+ CompactCertPart HashID = "ccp"
+ CompactCertSig HashID = "ccs"
+
AgreementSelector HashID = "AS"
BlockHeader HashID = "BH"
BalanceRecord HashID = "BR"