func (t *TreeMemoryCacheTransaction) Set(key chainhash.Hash, val chainhash.Hash) error {
	if !t.update {
		return errors.New("set called on View transaction")
	}
	t.dirtyKV[key] = val
	return nil
}
Set sets a value in the transaction.
package csmt
import (
	"errors"
	"fmt"
	"sync"
	"github.com/phoreproject/synapse/chainhash"
	"github.com/phoreproject/synapse/primitives"
)
type TreeMemoryCache struct {
	underlyingStore TreeDatabase
	dirty           map[chainhash.Hash]Node
	dirtyKV         map[chainhash.Hash]chainhash.Hash
	toRemove        map[chainhash.Hash]struct{}
	root            chainhash.Hash
	lock *sync.RWMutex
}
func (t *TreeMemoryCache) Hash() (*chainhash.Hash, error) {
	t.lock.RLock()
	defer t.lock.RUnlock()
	return &t.root, nil
}
func NewTreeMemoryCache(underlyingDatabase TreeDatabase) (*TreeMemoryCache, error) {
	var preRoot chainhash.Hash
	err := underlyingDatabase.View(func(tx TreeDatabaseTransaction) error {
		root, err := tx.Root()
		if err != nil {
			return err
		}
		if root == nil {
			preRoot = primitives.EmptyTree
			return nil
		}
		preRoot = root.GetHash()
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &TreeMemoryCache{
		underlyingStore: underlyingDatabase,
		root:            preRoot,
		dirty:           make(map[chainhash.Hash]Node),
		dirtyKV:         make(map[chainhash.Hash]chainhash.Hash),
		toRemove:        make(map[chainhash.Hash]struct{}),
		lock:            new(sync.RWMutex),
	}, nil
}
func (t *TreeMemoryCache) Update(cb func(TreeDatabaseTransaction) error) error {
	return t.underlyingStore.View(func(underlyingTx TreeDatabaseTransaction) error {
		t.lock.Lock()
		defer t.lock.Unlock()
		dirtyCopy := make(map[chainhash.Hash]Node)
		dirtyKVCopy := make(map[chainhash.Hash]chainhash.Hash)
		toRemoveCopy := make(map[chainhash.Hash]struct{})
		for k, v := range t.dirty {
			dirtyCopy[k] = v
		}
		for k, v := range t.dirtyKV {
			dirtyKVCopy[k] = v
		}
		for k := range t.toRemove {
			toRemoveCopy[k] = struct{}{}
		}
		tx := &TreeMemoryCacheTransaction{
			underlyingTransaction: underlyingTx,
			root:                  t.root,
			dirty:                 dirtyCopy,
			dirtyKV:               dirtyKVCopy,
			toRemove:              toRemoveCopy,
			update:                true,
		}
		err := cb(tx)
		if err != nil {
			return err
		}
		t.root = tx.root
		t.dirty = dirtyCopy
		t.dirtyKV = dirtyKVCopy
		t.toRemove = toRemoveCopy
		return nil
	})
}
func (t *TreeMemoryCache) View(cb func(TreeDatabaseTransaction) error) error {
	return t.underlyingStore.View(func(underlyingTx TreeDatabaseTransaction) error {
		t.lock.RLock()
		dirtyCopy := make(map[chainhash.Hash]Node)
		dirtyKVCopy := make(map[chainhash.Hash]chainhash.Hash)
		toRemoveCopy := make(map[chainhash.Hash]struct{})
		for k, v := range t.dirty {
			dirtyCopy[k] = v
		}
		for k, v := range t.dirtyKV {
			dirtyKVCopy[k] = v
		}
		for k := range t.toRemove {
			toRemoveCopy[k] = struct{}{}
		}
		t.lock.RUnlock()
		tx := &TreeMemoryCacheTransaction{
			underlyingTransaction: underlyingTx,
			root:                  t.root,
			dirty:                 dirtyCopy,
			dirtyKV:               dirtyKVCopy,
			toRemove:              toRemoveCopy,
			update:                false,
		}
		return cb(tx)
	})
}
func (t *TreeMemoryCache) Flush() error {
	return t.underlyingStore.Update(func(tx TreeDatabaseTransaction) error {
		for _, dirtyNode := range t.dirty {
			err := tx.SetNode(&dirtyNode)
			if err != nil {
				return err
			}
		}
		for c := range t.toRemove {
			err := tx.DeleteNode(c)
			if err != nil {
				return err
			}
		}
		root := t.root
		if !root.IsEqual(&primitives.EmptyTree) {
			rootNode, err := tx.GetNode(root)
			if err != nil {
				return err
			}
			err = tx.SetRoot(rootNode)
			if err != nil {
				return err
			}
		}
		for k, v := range t.dirtyKV {
			err := tx.Set(k, v)
			if err != nil {
				return err
			}
		}
		return nil
	})
}
func (t *TreeMemoryCache) UpdateUnderlying(cache TreeDatabase) error {
	t.lock.Lock()
	defer t.lock.Unlock()
	oldHash, err := t.Hash()
	if err != nil {
		return err
	}
	newHash, err := cache.Hash()
	if err != nil {
		return err
	}
	if !oldHash.IsEqual(newHash) {
		return fmt.Errorf("expected old store to match new store (old: %s, new: %s)", oldHash, newHash)
	}
	t.underlyingStore = cache
	return nil
}
func (t *TreeMemoryCache) GetUnderlying() TreeDatabase {
	t.lock.Lock()
	defer t.lock.Unlock()
	return t.underlyingStore
}
type TreeMemoryCacheTransaction struct {
	underlyingTransaction TreeDatabaseTransaction
	root chainhash.Hash
	toRemove map[chainhash.Hash]struct{}
	dirty    map[chainhash.Hash]Node
	dirtyKV  map[chainhash.Hash]chainhash.Hash
	update   bool
}
func (t *TreeMemoryCacheTransaction) Hash() (*chainhash.Hash, error) {
	return &t.root, nil
}
func (t *TreeMemoryCacheTransaction) Root() (*Node, error) {
	if t.root.IsEqual(&primitives.EmptyTree) {
		return nil, nil
	}
	if n, found := t.dirty[t.root]; found {
		return &n, nil
	}
	return t.underlyingTransaction.GetNode(t.root)
}
func (t *TreeMemoryCacheTransaction) SetRoot(n *Node) error {
	if !t.update {
		return errors.New("set root called on View transaction")
	}
	nodeHash := n.GetHash()
	if _, found := t.dirty[nodeHash]; !found {
		if _, err := t.underlyingTransaction.GetNode(nodeHash); err != nil {
			err := t.SetNode(n)
			if err != nil {
				return err
			}
		}
	}
	t.root = nodeHash
	return nil
}
func (t *TreeMemoryCacheTransaction) NewNode(left *Node, right *Node, subtreeHash chainhash.Hash) (*Node, error) {
	if !t.update {
		return nil, errors.New("new node called on View transaction")
	}
	var leftHash *chainhash.Hash
	var rightHash *chainhash.Hash
	if left != nil {
		lh := left.GetHash()
		leftHash = &lh
	}
	if right != nil {
		rh := right.GetHash()
		rightHash = &rh
	}
	newNode := &Node{
		value: subtreeHash,
		left:  leftHash,
		right: rightHash,
	}
	t.dirty[subtreeHash] = *newNode
	return newNode, nil
}
func (t *TreeMemoryCacheTransaction) NewSingleNode(key chainhash.Hash, value chainhash.Hash, subtreeHash chainhash.Hash) (*Node, error) {
	if !t.update {
		return nil, errors.New("new single node called on View transaction")
	}
	newNode := &Node{
		one:      true,
		oneKey:   &key,
		oneValue: &value,
		value:    subtreeHash,
	}
	t.dirty[subtreeHash] = *newNode
	return newNode, nil
}
func (t *TreeMemoryCacheTransaction) GetNode(c chainhash.Hash) (*Node, error) {
	if n, found := t.dirty[c]; found {
		return &n, nil
	}
	return t.underlyingTransaction.GetNode(c)
}
func (t *TreeMemoryCacheTransaction) SetNode(n *Node) error {
	if !t.update {
		return errors.New("set node called on View transaction")
	}
	nodeHash := n.GetHash()
	t.dirty[nodeHash] = *n
	return nil
}
func (t *TreeMemoryCacheTransaction) DeleteNode(c chainhash.Hash) error {
	if !t.update {
		return errors.New("delete node called on View transaction")
	}
	delete(t.dirty, c)
	t.toRemove[c] = struct{}{}
	return nil
}
func (t *TreeMemoryCacheTransaction) Get(key chainhash.Hash) (*chainhash.Hash, error) {
	if val, found := t.dirtyKV[key]; found {
		return &val, nil
	} else {
		return t.underlyingTransaction.Get(key)
	}
}