Documentation
¶
Overview ¶
Package trie implements a Patricia Merkle Trie.
A Patricia Merkle Trie can be used to cryptographically prove that a value is part of a set by only showing the nodes leading to that value.
It is similar to a Merkle Tree but it can easily and efficiently remove values from the set. It is also canonical, meaning that all tries containing the same values will be the same no matter which order the values were added or removed.
In the context of a blockchain, it is useful for light clients. By keeping all the information about the state in a Patricia Merkle Trie, the Merkle Root of the trie uniquely describes the current state of the application. Extracting a proof (the nodes leading to a value) gives you cryptographic evidence that the value is part of the current state. By adding a Merkle Root of the state to block headers, you only need the last block header and a Merkle Proof to prove the value of an account. It makes it very easy for a light node to easily sync up the accounts it is tracking. Without it it would need much more data to know without a doubt the value of an account.
It works similarly to a Radix trie:
A
\
l
\
i
/ \
c e
/ \
e n
As opposed to a tree, in a trie a value gives you its position the tree, and vice-versa. So a value will always have the same position in the trie. You can think of a value as a path in the trie. For instance, the path of the left leaf in the example is [A l i c e], giving you the value "Alice". This property of tries make them canonical -- a trie containing the same values will always be the same.
In this example we are using strings, so each branch can have as many children as the number of allowed characters. This number is said to be the radix of the tree.
Our implementation uses a radix of 16, corresponding to four bits of the value (a nibble). You can think of a nibble as a single hexadecimal character. For instance:
3
\
e
\
0
/ \
2 b
/ \
f c
In this example the left leaf represents the value 0x3e02f. To figure out the position of a node you can just look at the hexadecimal digits of the value.
This implementation allows you to store arbitrary data in the nodes. To avoid confusion, we call the path of a node the Key (not a value like previously), and the data it stores the Value, so it becomes more similar to a key-value database. In the previous example you could set an arbitrary value for the key 0x3e20f. More concretely, you could use the ID of an account as its key and the serialized account as its value, so in the previous example you could store the account whose ID is 0x3e02f in the left leaf.
In practice with arbitrary data you would end up with long series of branches having a single child, which is inefficient. So, like Ethereum, we introduce another type of node which contains a partial path that can be used to skip such nodes. For instance:
3
\
e
\
0
\
b
\
c
Can be represented by a single node of type edge containing the path between the two nodes:
3 [Edge: e0bc]
A Patricia Merkle Trie is a modified Radix trie that makes it cryptographically secure. A branch contains the hash of each of its child nodes (so one hash per child node, as opposed to one hash for all the child nodes like a Merkle Tree). The Merkle root is the hash of the root node. By having all the nodes leading to the desired value, you have cryptographic proof that it belongs in the trie.
Put, Delete, and Proof are O(k), where k is the length of the key. In practice they are faster because there will be fewer edges.
In this implementation, Get is O(1) because a key directly maps to its value in the database.
Index ¶
- Variables
- type Opt
- type Proof
- type ProofNode
- type Trie
- func (t *Trie) Check(ctx context.Context) error
- func (t *Trie) Commit() error
- func (t *Trie) Delete(key []byte) error
- func (t *Trie) Get(key []byte) ([]byte, error)
- func (t *Trie) IteratePrefix(prefix []byte) db.Iterator
- func (t *Trie) IterateRange(start, stop []byte) db.Iterator
- func (t *Trie) MerkleRoot() (multihash.Multihash, error)
- func (t *Trie) Proof(key []byte) (Proof, error)
- func (t *Trie) Put(key, value []byte) error
- func (t *Trie) Reset()
- func (t *Trie) SubtrieCheck(ctx context.Context, subtrieKey []byte) error
- func (t *Trie) SubtrieDelete(subtrieKey, key []byte) error
- func (t *Trie) SubtrieGet(subtrieKey, key []byte) ([]byte, error)
- func (t *Trie) SubtrieIteratePrefix(subtrieKey, prefix []byte) db.Iterator
- func (t *Trie) SubtrieIterateRange(subtrieKey, start, stop []byte) db.Iterator
- func (t *Trie) SubtrieMerkleRoot(subtrieKey []byte) (multihash.Multihash, error)
- func (t *Trie) SubtrieProof(subtrieKey, key []byte) (Proof, error)
- func (t *Trie) SubtriePut(subtrieKey, key, value []byte) error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidNodeType is returned when a node has an invalid type. ErrInvalidNodeType = errors.New("the node has an invalid type") // ErrInvalidNodeLen is returned when an encoded node has an invalid // number of bytes. ErrInvalidNodeLen = errors.New("the encoded node has an invalid number of bytes") )
var ( // ErrInvalidPathLen is returned when an encoded path has an invalid // number of bytes. ErrInvalidPathLen = errors.New("the encoded path has an invalid number of bytes") // ErrBufferToShort is returned when a given buffer is too short. ErrBufferToShort = errors.New("the buffer is too short") )
var ( // ErrProofEmpty is returned when the length of the proof is zero. ErrProofEmpty = errors.New("the proof is empty") // ErrChildNotFound is returned when the hash of a node was not found // in its parent. ErrChildNotFound = errors.New("the child node was not found") // ErrInvalidMerkleRoot is returned when the Merkle root of the proof // is invalid. ErrInvalidMerkleRoot = errors.New("the Merkle root is invalid") // ErrInvalidKey is returned when the value of the proof is invalid. ErrInvalidKey = errors.New("the key is invalid") // ErrInvalidValue is returned when the value of the proof is invalid. ErrInvalidValue = errors.New("the value is invalid") )
var ( // ErrInvalidHash is returned when a node has an unexpected hash. ErrInvalidHash = errors.New("the node's hash is invalid") )
var ( // ErrIteratorReleased is returned when trying to use the iterator // after it was released. ErrIteratorReleased = errors.New("the iterator was released") )
Functions ¶
This section is empty.
Types ¶
type Opt ¶
type Opt func(*Trie)
Opt is an option for a Patricia Merkle Trie.
func OptCacheSize ¶
OptCacheSize sets the number of nodes kept in the cache.
The default value is 1000.
func OptDB ¶
func OptDB(db db.ReadWriter) Opt
OptDB sets the database. By default it uses an in-memory map.
func OptHashCode ¶
OptHashCode sets the hash algorithm (see Multihash for codes).
The default value is SHA2_256.
type Proof ¶
type Proof []ProofNode
Proof contains evidence that a value is in a Patricia Merkle Trie.
func NewProofFromProto ¶
NewProofFromProto converts a slice of protobuf messages to a proof.
func (Proof) MerkleRoot ¶
MerkleRoot returns the Merkle root contained in the proof.
type ProofNode ¶
type ProofNode struct {
// Key and Value are only set for value branches and leaves.
Key []byte
Value []byte
// ChildHashes contains the hash of the child nodes if the node is a
// branch.
ChildHashes []multihash.Multihash
}
ProofNode describes a node in a proof.
func NewProofNodeFromProto ¶
NewProofNodeFromProto converts protobuf message to a proof node.
type Trie ¶
type Trie struct {
// contains filtered or unexported fields
}
Trie represents a Patricia Merkle Trie.
It implements db.ReadWriter and db.Ranger, though currently it doesn't support empty values, so calling Put(key, nil) is equivalent to calling Delete(key).
TODO: this could be changed by adding a null flag to nodes.
The underlying database is not modified until Commit() is called.
func (*Trie) Check ¶
Check makes sure that a trie is properly structured. This can take a very long time.
func (*Trie) Commit ¶
Commit applies all the changes to the database since the last call to Commit() or Reset().
func (*Trie) Delete ¶
Delete removes the value for the given key. Deleting a non-existing key is a NOP.
func (*Trie) Get ¶
Get gets the value of the given key.
The value is not copied, so the caller should copy it if it plans on using it elsewhere.
func (*Trie) IteratePrefix ¶
IteratePrefix creates an iterator that iterates over all the keys that begin with the given prefix. Remember to call Release() on the iterator.
func (*Trie) IterateRange ¶
IterateRange creates an iterator that iterates from the given start key (inclusive) up to the given stop key (exclusive). Remember to call Release() on the iterator.
func (*Trie) MerkleRoot ¶
MerkleRoot returns the hash of the root node.
func (*Trie) Put ¶
Put sets the value of the given key. Putting a nil or empty value is the same as deleting it.
The value is not copied, so the caller should copy it beforehand if it plans on using it elsewhere.
func (*Trie) SubtrieCheck ¶
SubtrieCheck makes sure that a subtrie is properly structured. This can take a very long time.
func (*Trie) SubtrieDelete ¶
SubtrieDelete removes the value for the given key relative to the given subtrie key. Deleting a non-existing key is a NOP.
func (*Trie) SubtrieGet ¶
SubtrieGet gets the value of the given key relative to the given subtrie key.
func (*Trie) SubtrieIteratePrefix ¶
SubtrieIteratePrefix creates an iterator that iterates over all the keys that begin with the given prefix relative to the given subtrie. Remember to call Release() on the iterator.
func (*Trie) SubtrieIterateRange ¶
SubtrieIterateRange creates an iterator that iterates from the given start key (inclusive) up to the given stop key (exclusive) relative to the given subtrie key. Remember to call Release() on the iterator.
func (*Trie) SubtrieMerkleRoot ¶
SubtrieMerkleRoot returns the hash of the given subtrie key.
func (*Trie) SubtrieProof ¶
SubtrieProof returns a proof of the value for the given key relative to the given subtrie key.
func (*Trie) SubtriePut ¶
SubtriePut sets the value of the given key relative to the given subtrie key. Putting a nil or empty value is the same as deleting it.
The value is not copied, so the caller should copy it beforehand if it plans on using it elsewhere.