Skip to content

Commit

Permalink
Merge pull request #1455 from onetechnical/onetechnical/relstable2.1.4
Browse files Browse the repository at this point in the history
Onetechnical/relstable2.1.4
  • Loading branch information
algojohnlee authored Aug 27, 2020
2 parents 30c8dd6 + 816bc8a commit 65e359d
Show file tree
Hide file tree
Showing 34 changed files with 1,650 additions and 148 deletions.
2 changes: 1 addition & 1 deletion buildnumber.dat
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3
4
16 changes: 14 additions & 2 deletions cmd/pingpong/runCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"context"
"encoding/base64"
"fmt"
"github.com/algorand/go-algorand/data/transactions/logic"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -29,6 +28,7 @@ import (

"github.com/spf13/cobra"

"github.com/algorand/go-algorand/data/transactions/logic"
"github.com/algorand/go-algorand/libgoal"
"github.com/algorand/go-algorand/shared/pingpong"
)
Expand Down Expand Up @@ -59,6 +59,7 @@ var groupSize uint32
var numAsset uint32
var numApp uint32
var appProgOps uint32
var rekey bool

func init() {
rootCmd.AddCommand(runCmd)
Expand Down Expand Up @@ -91,6 +92,7 @@ func init() {
runCmd.Flags().Uint32Var(&numApp, "numapp", 0, "The number of apps each account opts in to")
runCmd.Flags().Uint32Var(&appProgOps, "appprogops", 0, "The approximate number of TEAL operations to perform in each ApplicationCall transaction")
runCmd.Flags().BoolVar(&randomLease, "randomlease", false, "set the lease to contain a random value")
runCmd.Flags().BoolVar(&rekey, "rekey", false, "Create RekeyTo transactions. Requires groupsize=2 and any of random flags exc random dst")
}

var runCmd = &cobra.Command{
Expand Down Expand Up @@ -254,7 +256,17 @@ var runCmd = &cobra.Command{
}

if numAsset != 0 && numApp != 0 {
reportErrorf("only one of numapp and numasset may be specified")
reportErrorf("only one of numapp and numasset may be specified\n")
}

if rekey {
cfg.Rekey = rekey
if !cfg.RandomLease && !cfg.RandomNote && !cfg.RandomizeFee && !cfg.RandomizeAmt {
reportErrorf("RandomNote, RandomLease, RandomizeFee or RandomizeAmt must be used with rekeying\n")
}
if cfg.GroupSize != 2 {
reportErrorf("Rekeying requires txn groups of size 2\n")
}
}

reportInfof("Preparing to initialize PingPong with config:\n")
Expand Down
5 changes: 3 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ type Local struct {
// NetworkProtocolVersion overrides network protocol version ( if present )
NetworkProtocolVersion string `version[6]:""`

// CatchpointInterval set the interval at which catchpoint are being generated.
// CatchpointInterval sets the interval at which catchpoint are being generated. Setting this to 0 disables the catchpoint from being generated.
// See CatchpointTracking for more details.
CatchpointInterval uint64 `version[7]:"10000"`

// CatchpointFileHistoryLength defines how many catchpoint files we want to store back.
Expand Down Expand Up @@ -330,7 +331,7 @@ type Local struct {
OptimizeAccountsDatabaseOnStartup bool `version[10]:"false"`

// CatchpointTracking determines if catchpoints are going to be tracked. The value is interpreted as follows:
// A of -1 means "don't track catchpoints".
// A value of -1 means "don't track catchpoints".
// A value of 1 means "track catchpoints as long as CatchpointInterval is also set to a positive non-zero value". If CatchpointInterval <= 0, no catchpoint tracking would be performed.
// A value of 0 means automatic, which is the default value. In this mode, a non archival node would not track the catchpoints, and an archival node would track the catchpoints as long as CatchpointInterval > 0.
// Other values of CatchpointTracking would give a warning in the log file, and would behave as if the default value was provided.
Expand Down
28 changes: 28 additions & 0 deletions crypto/merklearray/array.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 <https://www.gnu.org/licenses/>.

package merklearray

import (
"github.com/algorand/go-algorand/crypto"
)

// An Array represents a dense array of leaf elements that are
// combined into a Merkle tree. Each element must be hashable.
type Array interface {
Length() uint64
Get(pos uint64) (crypto.Hashable, error)
}
56 changes: 56 additions & 0 deletions crypto/merklearray/layer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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 <https://www.gnu.org/licenses/>.

package merklearray

import (
"github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/protocol"
)

// A layer of the Merkle tree consists of a dense array of hashes at that
// level of the tree. Hashes beyond the end of the array (e.g., if the
// number of leaves is not an exact power of 2) are implicitly zero.
type layer []crypto.Digest

// A pair represents an internal node in the Merkle tree.
type pair struct {
l crypto.Digest
r crypto.Digest
}

func (p *pair) ToBeHashed() (protocol.HashID, []byte) {
var buf [2 * crypto.DigestSize]byte
copy(buf[:crypto.DigestSize], p.l[:])
copy(buf[crypto.DigestSize:], p.r[:])
return protocol.MerkleArrayNode, buf[:]
}

// up takes a layer representing some level in the tree,
// and returns the next-higher level in the tree,
// represented as a layer.
func (l layer) up() layer {
res := make(layer, (uint64(len(l))+1)/2)
for i := 0; i < len(l); i += 2 {
var p pair
p.l = l[i]
if i+1 < len(l) {
p.r = l[i+1]
}
res[i/2] = crypto.HashObj(&p)
}
return res
}
178 changes: 178 additions & 0 deletions crypto/merklearray/merkle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// 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 <https://www.gnu.org/licenses/>.

package merklearray

import (
"fmt"
"sort"

"github.com/algorand/go-algorand/crypto"
)

// Tree is a Merkle tree, represented by layers of nodes (hashes) in the tree
// at each height.
type Tree struct {
// Level 0 is the leaves.
levels []layer
}

func (tree *Tree) topLayer() layer {
return tree.levels[len(tree.levels)-1]
}

// Build constructs a Merkle tree given an array.
func Build(array Array) (*Tree, error) {
tree := &Tree{}

var leaves layer
arraylen := array.Length()
for i := uint64(0); i < arraylen; i++ {
data, err := array.Get(i)
if err != nil {
return nil, err
}

leaves = append(leaves, crypto.HashObj(data))
}

if arraylen > 0 {
tree.levels = []layer{leaves}

for len(tree.topLayer()) > 1 {
tree.levels = append(tree.levels, tree.topLayer().up())
}
}

return tree, nil
}

// Root returns the root hash of the tree.
func (tree *Tree) Root() crypto.Digest {
// Special case: commitment to zero-length array
if len(tree.levels) == 0 {
var zero crypto.Digest
return zero
}

return tree.topLayer()[0]
}

const validateProof = false

// Prove constructs a proof for some set of positions in the array that was
// used to construct the tree.
func (tree *Tree) Prove(idxs []uint64) ([]crypto.Digest, error) {
if len(idxs) == 0 {
return nil, nil
}

// Special case: commitment to zero-length array
if len(tree.levels) == 0 {
return nil, fmt.Errorf("proving in zero-length commitment")
}

sort.Slice(idxs, func(i, j int) bool { return idxs[i] < idxs[j] })

pl := make(partialLayer, 0, len(idxs))
for _, pos := range idxs {
if pos >= uint64(len(tree.levels[0])) {
return nil, fmt.Errorf("pos %d larger than leaf count %d", pos, len(tree.levels[0]))
}

// Discard duplicates
if len(pl) > 0 && pl[len(pl)-1].pos == pos {
continue
}

pl = append(pl, layerItem{
pos: pos,
hash: tree.levels[0][pos],
})
}

s := &siblings{
tree: tree,
}

for l := uint64(0); l < uint64(len(tree.levels)-1); l++ {
var err error
pl, err = pl.up(s, l, validateProof)
if err != nil {
return nil, err
}
}

// Confirm that we got the same root hash
if len(pl) != 1 {
return nil, fmt.Errorf("internal error: partial layer produced %d hashes", len(pl))
}

if validateProof {
computedroot := pl[0]
if computedroot.pos != 0 || computedroot.hash != tree.topLayer()[0] {
return nil, fmt.Errorf("internal error: root mismatch during proof")
}
}

return s.hints, nil
}

// Verify ensures that the positions in elems correspond to the hashes of their respective
// crypto.Hashable objects in a tree with the given root hash. The proof is expected to
// be the proof returned by Prove().
func Verify(root crypto.Digest, elems map[uint64]crypto.Hashable, proof []crypto.Digest) error {
if len(elems) == 0 {
if len(proof) != 0 {
return fmt.Errorf("non-empty proof for empty set of elements")
}

return nil
}

pl := make(partialLayer, 0, len(elems))
for pos, elem := range elems {
pl = append(pl, layerItem{
pos: pos,
hash: crypto.HashObj(elem),
})
}

sort.Slice(pl, func(i, j int) bool { return pl[i].pos < pl[j].pos })

s := &siblings{
hints: proof,
}

for l := uint64(0); len(s.hints) > 0 || len(pl) > 1; l++ {
var err error
pl, err = pl.up(s, l, true)
if err != nil {
return err
}

if l > 64 {
return fmt.Errorf("Verify exceeded 64 levels, more than 2^64 leaves not supported")
}
}

computedroot := pl[0]
if computedroot.pos != 0 || computedroot.hash != root {
return fmt.Errorf("root mismatch")
}

return nil
}
Loading

0 comments on commit 65e359d

Please sign in to comment.