Documentation ¶
Overview ¶
This package contains the tree structure used in the Utreexo accumulator library It is meant to be coin agnostic. Bitcoin specific code can be found in 'cmd/'.
The basic flow for using the accumulator package goes as such:
Initialize Forest or Pollard. // inits a Forest in memory // pass a file in to init a forest on disk forest := accumulator.NewForest(nil)
// declare pollard. No init function for pollard var pollard accumulator.Pollard
Call the Modify() methods for each one. // Adds and Deletes leaves from the Forest forest.Modify(leavesToAdd, leavesToDelete)
// Adds and Deletes leaves from the Pollard pollard.Modify(leavesToAdd, leavesToDelete)
Thats it!
To add transaction verification, existence of the transaction needs to be checked before to make sure the transaction exists. With Forest, this is done with FindLeaf() which is a wrapper around Golang maps. This is ok since Forest is aware of every tx in existence.
With Pollard, VerifyBatchProof() method needs to be called to check for tree tops. If it verifies, this means that the transaction(s) that was sent over exists.
Accumulator in detail:
Jargon: In parts of the forest code you'll see these terminology being used.
Perfect tree - A tree with 2**x leaves. All trees in Utreexo are perfect. Root - The top most parts of the tree. A Utreexo tree may have more than 1 top unlike a Merkle tree. Parent - The hash of two leaves concatenated. Sibling - The other leaf that shares the same parent. Aunt - The sibling of the parent leaf. Cousin - The children of the parent leaf's sibling.
Forest is a representation of a "full" Utreexo tree. The Forest implementation would be the Utreexo bridge node. "Full" means that all the hashes of the tree is stored.
This is done as either:
- byte slice
- contiguous data in a file
The ordering of the Utreexo tree is done in a similar fashion to that of a 2x2 array in row-major order. A Utreexo tree with 8 leaves would look like:
06 |-------\ 04......05 |---\...|---\ 00..01..02..03
In the byte slice, this would be represented as such:
byte[]{00, 01, 02, 03, 04, 05, 06}
It would be saved in the same fashion in the file.
For perfect trees, this is simple. However, for trees that aren't perfect, this is a little different.
If one leaf gets added to the above tree, a new 8 leave tree is initialized by Remap(). The new tree looks like such.
em |---------------\ 12..............em |-------\.......|-------\ 08......09......em......em |---\...|---\...|---\...|---\ 00..01..02..03..04..em..em..em
em stands for empty and the leaf is initialized to either:
- uint64 of 0s for RAM
- whatever data there was on disk for on disk
The em leaves still have positions but just hold values of 0s.
Remap() is never called for when a leaf is deleted. For example, the forest will hold an empty tree when leaf 04 is deleted in the above tree. It will look like below:
em |---------------\ 12..............em |-------\.......|-------\ 08......09......em......em |---\...|---\...|---\...|---\ 00..01..02..03..em..em..em..em
This is not a bug and is done on purpose for efficiency reasons. If a tree is adding and deleting between a leaf number of 2**x, it will cause an io bottleneck as the tree on the right is constantly deleted and re-initialized.
This will sometimes cause the Forest to have a total row value that is 1 greater than it actually is.
Pollard:
Pollard is a representation of a "sparse" Utreexo tree. "Sparse" means that not all leaves are stored. The above tree with 8 leaves may look like such.
14 |---------------\ **..............** |-------\.......|-------\ **......**......**......** |---\...|---\...|---\...|---\ **..**..**..**..**..**..**..**
Some leaves may be saved for caching purposes but they do not have to be.
Pollard uses binary tree pointers instead of a byte slice. Each hash is of type PolNode. In a tree below, each number would represent a PolNode.
14 |---------------\ 12..............13 |-------\.......|-------\ 08......09......10......11 |---\...|---\...|---\...|---\ 00..01..02..03..04..05..06..07
Unlike traditional binary trees, the parent does NOT point to its children. It points to its nieces.
Number 08 PolNode would point to leaves 02 and 03. 12 Polnode would point to 10 and 11. This is done for efficiency reasons as Utreexo Pollard uses the Aunt to verify whether a leaf exists or not. Parents can be computed from its children but an Aunt needs to be provided.
For example, with a tree like below, to prove the inclusion of 03, the prover needs to provide 02, 03, 08, 13.
14 |---------------\ 12..............13 |-------\.......|-------\ 08......09......10......11 |---\...|---\...|---\...|---\ 00..01..02..03..04..05..06..07
A Pollard node is not aware of each individual PolNode's position in the tree. This is different from Forest, as Forest is aware of every single leaf and its position. Getting the position of the Polnode is done through grabPos()/descendToPos() methods.
Index ¶
- Constants
- Variables
- func BinString(leaves uint64) string
- type BatchProof
- type Forest
- func (f *Forest) Add(adds []Leaf)
- func (f *Forest) BuildUndoData(numadds uint64, dels []uint64) *undoBlock
- func (f *Forest) FindLeaf(leaf Hash) bool
- func (f *Forest) Modify(adds []Leaf, dels []uint64) (*undoBlock, error)
- func (f *Forest) PrintPositionMap() string
- func (f *Forest) Prove(wanted Hash) (Proof, error)
- func (f *Forest) ProveBatch(hs []Hash) (BatchProof, error)
- func (f *Forest) ProveMany(hs []Hash) ([]Proof, error)
- func (f *Forest) ReconstructStats() (uint64, uint8)
- func (f *Forest) Stats() string
- func (f *Forest) ToString() string
- func (f *Forest) Undo(ub undoBlock) error
- func (f *Forest) Verify(p Proof) bool
- func (f *Forest) VerifyBatchProof(bp BatchProof) bool
- func (f *Forest) VerifyMany(ps []Proof) bool
- func (f *Forest) WriteForestToDisk(dumpFile *os.File) error
- func (f *Forest) WriteMiscData(miscForestFile *os.File) error
- type ForestData
- type Hash
- type Leaf
- type MiniHash
- type PatriciaLookup
- type PatriciaNode
- type Pollard
- func (p *Pollard) IngestBatchProof(bp BatchProof) error
- func (p *Pollard) Modify(adds []Leaf, dels []uint64) error
- func (p *Pollard) PosMapSanity() error
- func (p *Pollard) ProveBatch(hs []Hash) (BatchProof, error)
- func (p *Pollard) ReconstructStats() (uint64, uint8)
- func (p *Pollard) RestorePollard(r io.Reader) error
- func (p *Pollard) Stats() string
- func (p *Pollard) ToString() string
- func (p *Pollard) WritePollard(w io.Writer) error
- type Proof
- type SimChain
Constants ¶
const ( ErrorNotEnoughTrees uint32 = iota ErrorNoPollardNode )
Variables ¶
var ErrorStrings = map[uint32]error{ ErrorNotEnoughTrees: fmt.Errorf("ErrorNotEnoughTrees"), ErrorNoPollardNode: fmt.Errorf("ErrorNoPollardNode"), }
Functions ¶
Types ¶
type BatchProof ¶
BatchProof :
func FromBytesBatchProof ¶
func FromBytesBatchProof(b []byte) (BatchProof, error)
FromBytesBatchProof gives a block proof back from the serialized bytes
func (*BatchProof) Reconstruct ¶
Reconstruct takes a number of leaves and rows, and turns a block proof back into a partial proof tree. Should leave bp intact
func (*BatchProof) SortTargets ¶
func (bp *BatchProof) SortTargets()
ToString for debugging, shows the blockproof
func (*BatchProof) ToBytes ¶
func (bp *BatchProof) ToBytes() []byte
ToBytes give the bytes for a BatchProof. It errors out silently because I don't think the binary.Write errors ever actually happen
func (*BatchProof) ToString ¶
func (bp *BatchProof) ToString() string
ToString for debugging, shows the blockproof
type Forest ¶
type Forest struct { // HistoricHashes represents how many hashes this forest has computed // // Meant for testing / benchmarking HistoricHashes uint64 // TimeRem represents how long Remove() function took // // Meant for testing / benchmarking TimeRem time.Duration // TimeMST represents how long the moveSubTree() function took // // Meant for testing / benchmarking TimeMST time.Duration // TimeInHash represents how long the hash operations (reHash) took // // Meant for testing / benchmarking TimeInHash time.Duration // TimeInProve represents how long the Prove operations took // // Meant for testing / benchmarking TimeInProve time.Duration // TimeInVerify represents how long the verify operations took // // Meant for testing / benchmarking TimeInVerify time.Duration // contains filtered or unexported fields }
Forest is the entire accumulator of the UTXO set as either a: 1) slice if the forest is stored in memory. 2) single file if the forest is stored in disk. A leaf represents a UTXO with additional data for verification. This leaf is numbered from bottom left to right. Example of a forest with 4 numLeaves:
06 |------\ 04......05 |---\...|---\ 00..01..02..03
04 is the concatenation and the hash of 00 and 01. 06 is the root This tree would have a row of 2.
func RestoreForest ¶
func RestoreForest( miscForestFile *os.File, forestFile *os.File, toRAM, cached bool) (*Forest, error)
RestoreForest restores the forest on restart. Needed when resuming after exiting. miscForestFile is where numLeaves and rows is stored
func (*Forest) BuildUndoData ¶
BuildUndoData makes an undoBlock from the same data that you'd give to Modify
func (*Forest) Modify ¶
Modify changes the forest, adding and deleting leaves and updating internal nodes. Note that this does not modify in place! All deletes occur simultaneous with adds, which show up on the right. Also, the deletes need there to be correct proof data, so you should first call Verify().
func (*Forest) PrintPositionMap ¶
func (*Forest) ProveBatch ¶
func (f *Forest) ProveBatch(hs []Hash) (BatchProof, error)
ProveBatch gets proofs (in the form of a node slice) for a bunch of leaves The ordering of Targets is the same as the ordering of hashes given as argument. NOTE However targets will need to be sorted before using the proof! TODO the elements to be proven should not be included in the proof.
func (*Forest) ReconstructStats ¶
TODO remove, only here for testing
func (*Forest) VerifyBatchProof ¶
func (f *Forest) VerifyBatchProof(bp BatchProof) bool
VerifyBatchProof :
func (*Forest) VerifyMany ¶
VerifyMany is like verify but more.
func (*Forest) WriteForestToDisk ¶
WriteForestToDisk writes the whole forest to disk this only makes sense to do if the forest is in ram. So it'll return an error if it's not a ramForestData
type ForestData ¶
type ForestData interface {
// contains filtered or unexported methods
}
ForestData is the thing that holds all the hashes in the forest. Could be in a file, or in ram, or maybe something else.
type Leaf ¶
Leaf contains a hash and a hint about whether it should be saved to memory or not during ibdsim.
type PatriciaLookup ¶
type PatriciaLookup struct {
// contains filtered or unexported fields
}
type PatriciaNode ¶
type PatriciaNode struct {
// contains filtered or unexported fields
}
type Pollard ¶
type Pollard struct { Lookahead int32 // remember leafs below this TTL // contains filtered or unexported fields }
Pollard :
func NewFullPollard ¶
func NewFullPollard() Pollard
NewFullPollard gives you a Pollard with an activated
func (*Pollard) IngestBatchProof ¶
func (p *Pollard) IngestBatchProof(bp BatchProof) error
IngestBatchProof populates the Pollard with all needed data to delete the targets in the block proof
func (*Pollard) Modify ¶
Modify is the main function that deletes then adds elements to the accumulator
func (*Pollard) PosMapSanity ¶
PosMapSanity is costly / slow: check that everything in posMap is correct
func (*Pollard) ProveBatch ¶
func (p *Pollard) ProveBatch(hs []Hash) (BatchProof, error)
ProveBatch but for pollard. Now getting really obvious that forest and pollard should both satisfy some kind of utreexo-like interface. And maybe forest shouldn't be called forest. Anyway do that after this.
func (*Pollard) ReconstructStats ¶
TODO remove Temporary -- returns numleaves and row so that batch proofs can be reconstructed and hashes can be matches. Replace this with proofs that do not include the things being proven, and take the proved leaves as a separate argument
type Proof ¶
type Proof struct { Position uint64 // where at the bottom of the tree it sits Payload Hash // hash of the thing itself (what's getting proved) Siblings []Hash // slice of siblings up to a root }
Proof :