github.com/ledgerwatch/erigon-lib@v1.0.0/commitment/commitment.go (about)

     1  package commitment
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"hash"
     8  	"math/bits"
     9  	"strings"
    10  
    11  	"golang.org/x/crypto/sha3"
    12  
    13  	"github.com/ledgerwatch/erigon-lib/common/length"
    14  )
    15  
    16  // Trie represents commitment variant.
    17  type Trie interface {
    18  	// RootHash produces root hash of the trie
    19  	RootHash() (hash []byte, err error)
    20  
    21  	// Variant returns commitment trie variant
    22  	Variant() TrieVariant
    23  
    24  	// Reset Drops everything from the trie
    25  	Reset()
    26  
    27  	ReviewKeys(pk, hk [][]byte) (rootHash []byte, branchNodeUpdates map[string]BranchData, err error)
    28  
    29  	ProcessUpdates(pk, hk [][]byte, updates []Update) (rootHash []byte, branchNodeUpdates map[string]BranchData, err error)
    30  
    31  	ResetFns(
    32  		branchFn func(prefix []byte) ([]byte, error),
    33  		accountFn func(plainKey []byte, cell *Cell) error,
    34  		storageFn func(plainKey []byte, cell *Cell) error,
    35  	)
    36  
    37  	// Makes trie more verbose
    38  	SetTrace(bool)
    39  }
    40  
    41  type TrieVariant string
    42  
    43  const (
    44  	// VariantHexPatriciaTrie used as default commitment approach
    45  	VariantHexPatriciaTrie TrieVariant = "hex-patricia-hashed"
    46  	// VariantBinPatriciaTrie - Experimental mode with binary key representation
    47  	VariantBinPatriciaTrie TrieVariant = "bin-patricia-hashed"
    48  )
    49  
    50  func InitializeTrie(tv TrieVariant) Trie {
    51  	switch tv {
    52  	case VariantBinPatriciaTrie:
    53  		return NewBinPatriciaHashed(length.Addr, nil, nil, nil)
    54  	case VariantHexPatriciaTrie:
    55  		fallthrough
    56  	default:
    57  		return NewHexPatriciaHashed(length.Addr, nil, nil, nil)
    58  	}
    59  }
    60  
    61  type PartFlags uint8
    62  
    63  const (
    64  	HashedKeyPart    PartFlags = 1
    65  	AccountPlainPart PartFlags = 2
    66  	StoragePlainPart PartFlags = 4
    67  	HashPart         PartFlags = 8
    68  )
    69  
    70  type BranchData []byte
    71  
    72  func (branchData BranchData) String() string {
    73  	touchMap := binary.BigEndian.Uint16(branchData[0:])
    74  	afterMap := binary.BigEndian.Uint16(branchData[2:])
    75  	pos := 4
    76  	var sb strings.Builder
    77  	var cell Cell
    78  	fmt.Fprintf(&sb, "touchMap %016b, afterMap %016b\n", touchMap, afterMap)
    79  	for bitset, j := touchMap, 0; bitset != 0; j++ {
    80  		bit := bitset & -bitset
    81  		nibble := bits.TrailingZeros16(bit)
    82  		fmt.Fprintf(&sb, "   %x => ", nibble)
    83  		if afterMap&bit == 0 {
    84  			sb.WriteString("{DELETED}\n")
    85  		} else {
    86  			fieldBits := PartFlags(branchData[pos])
    87  			pos++
    88  			var err error
    89  			if pos, err = cell.fillFromFields(branchData, pos, fieldBits); err != nil {
    90  				// This is used for test output, so ok to panic
    91  				panic(err)
    92  			}
    93  			sb.WriteString("{")
    94  			var comma string
    95  			if cell.downHashedLen > 0 {
    96  				fmt.Fprintf(&sb, "hashedKey=[%x]", cell.downHashedKey[:cell.downHashedLen])
    97  				comma = ","
    98  			}
    99  			if cell.apl > 0 {
   100  				fmt.Fprintf(&sb, "%saccountPlainKey=[%x]", comma, cell.apk[:cell.apl])
   101  				comma = ","
   102  			}
   103  			if cell.spl > 0 {
   104  				fmt.Fprintf(&sb, "%sstoragePlainKey=[%x]", comma, cell.spk[:cell.spl])
   105  				comma = ","
   106  			}
   107  			if cell.hl > 0 {
   108  				fmt.Fprintf(&sb, "%shash=[%x]", comma, cell.h[:cell.hl])
   109  			}
   110  			sb.WriteString("}\n")
   111  		}
   112  		bitset ^= bit
   113  	}
   114  	return sb.String()
   115  }
   116  
   117  func EncodeBranch(bitmap, touchMap, afterMap uint16, retriveCell func(nibble int, skip bool) (*Cell, error)) (branchData BranchData, lastNibble int, err error) {
   118  	branchData = make(BranchData, 0, 32)
   119  	var bitmapBuf [binary.MaxVarintLen64]byte
   120  
   121  	binary.BigEndian.PutUint16(bitmapBuf[0:], touchMap)
   122  	binary.BigEndian.PutUint16(bitmapBuf[2:], afterMap)
   123  
   124  	branchData = append(branchData, bitmapBuf[:4]...)
   125  
   126  	for bitset, j := afterMap, 0; bitset != 0; j++ {
   127  		bit := bitset & -bitset
   128  		nibble := bits.TrailingZeros16(bit)
   129  		for i := lastNibble; i < nibble; i++ {
   130  			if _, err := retriveCell(i, true /* skip */); err != nil {
   131  				return nil, 0, err
   132  			} // only writes 0x80 into hasher
   133  		}
   134  		lastNibble = nibble + 1
   135  
   136  		cell, err := retriveCell(nibble, false)
   137  		if err != nil {
   138  			return nil, 0, err
   139  		}
   140  
   141  		if bitmap&bit != 0 {
   142  			var fieldBits PartFlags
   143  			if cell.extLen > 0 && cell.spl == 0 {
   144  				fieldBits |= HashedKeyPart
   145  			}
   146  			if cell.apl > 0 {
   147  				fieldBits |= AccountPlainPart
   148  			}
   149  			if cell.spl > 0 {
   150  				fieldBits |= StoragePlainPart
   151  			}
   152  			if cell.hl > 0 {
   153  				fieldBits |= HashPart
   154  			}
   155  			branchData = append(branchData, byte(fieldBits))
   156  			if cell.extLen > 0 && cell.spl == 0 {
   157  				n := binary.PutUvarint(bitmapBuf[:], uint64(cell.extLen))
   158  				branchData = append(branchData, bitmapBuf[:n]...)
   159  				branchData = append(branchData, cell.extension[:cell.extLen]...)
   160  			}
   161  			if cell.apl > 0 {
   162  				n := binary.PutUvarint(bitmapBuf[:], uint64(cell.apl))
   163  				branchData = append(branchData, bitmapBuf[:n]...)
   164  				branchData = append(branchData, cell.apk[:cell.apl]...)
   165  			}
   166  			if cell.spl > 0 {
   167  				n := binary.PutUvarint(bitmapBuf[:], uint64(cell.spl))
   168  				branchData = append(branchData, bitmapBuf[:n]...)
   169  				branchData = append(branchData, cell.spk[:cell.spl]...)
   170  			}
   171  			if cell.hl > 0 {
   172  				n := binary.PutUvarint(bitmapBuf[:], uint64(cell.hl))
   173  				branchData = append(branchData, bitmapBuf[:n]...)
   174  				branchData = append(branchData, cell.h[:cell.hl]...)
   175  			}
   176  		}
   177  		bitset ^= bit
   178  	}
   179  	return branchData, lastNibble, nil
   180  }
   181  
   182  // ExtractPlainKeys parses branchData and extract the plain keys for accounts and storage in the same order
   183  // they appear witjin the branchData
   184  func (branchData BranchData) ExtractPlainKeys() (accountPlainKeys [][]byte, storagePlainKeys [][]byte, err error) {
   185  	touchMap := binary.BigEndian.Uint16(branchData[0:])
   186  	afterMap := binary.BigEndian.Uint16(branchData[2:])
   187  	pos := 4
   188  	for bitset, j := touchMap&afterMap, 0; bitset != 0; j++ {
   189  		bit := bitset & -bitset
   190  		fieldBits := PartFlags(branchData[pos])
   191  		pos++
   192  		if fieldBits&HashedKeyPart != 0 {
   193  			l, n := binary.Uvarint(branchData[pos:])
   194  			if n == 0 {
   195  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for hashedKey len")
   196  			} else if n < 0 {
   197  				return nil, nil, fmt.Errorf("extractPlainKeys value overflow for hashedKey len")
   198  			}
   199  			pos += n
   200  			if len(branchData) < pos+int(l) {
   201  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for hashedKey")
   202  			}
   203  			if l > 0 {
   204  				pos += int(l)
   205  			}
   206  		}
   207  		if fieldBits&AccountPlainPart != 0 {
   208  			l, n := binary.Uvarint(branchData[pos:])
   209  			if n == 0 {
   210  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for accountPlainKey len")
   211  			} else if n < 0 {
   212  				return nil, nil, fmt.Errorf("extractPlainKeys value overflow for accountPlainKey len")
   213  			}
   214  			pos += n
   215  			if len(branchData) < pos+int(l) {
   216  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for accountPlainKey")
   217  			}
   218  			accountPlainKeys = append(accountPlainKeys, branchData[pos:pos+int(l)])
   219  			if l > 0 {
   220  				pos += int(l)
   221  			}
   222  		}
   223  		if fieldBits&StoragePlainPart != 0 {
   224  			l, n := binary.Uvarint(branchData[pos:])
   225  			if n == 0 {
   226  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for storagePlainKey len")
   227  			} else if n < 0 {
   228  				return nil, nil, fmt.Errorf("extractPlainKeys value overflow for storagePlainKey len")
   229  			}
   230  			pos += n
   231  			if len(branchData) < pos+int(l) {
   232  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for storagePlainKey")
   233  			}
   234  			storagePlainKeys = append(storagePlainKeys, branchData[pos:pos+int(l)])
   235  			if l > 0 {
   236  				pos += int(l)
   237  			}
   238  		}
   239  		if fieldBits&HashPart != 0 {
   240  			l, n := binary.Uvarint(branchData[pos:])
   241  			if n == 0 {
   242  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for hash len")
   243  			} else if n < 0 {
   244  				return nil, nil, fmt.Errorf("extractPlainKeys value overflow for hash len")
   245  			}
   246  			pos += n
   247  			if len(branchData) < pos+int(l) {
   248  				return nil, nil, fmt.Errorf("extractPlainKeys buffer too small for hash")
   249  			}
   250  			if l > 0 {
   251  				pos += int(l)
   252  			}
   253  		}
   254  		bitset ^= bit
   255  	}
   256  	return
   257  }
   258  
   259  func (branchData BranchData) ReplacePlainKeys(accountPlainKeys [][]byte, storagePlainKeys [][]byte, newData []byte) (BranchData, error) {
   260  	var numBuf [binary.MaxVarintLen64]byte
   261  	touchMap := binary.BigEndian.Uint16(branchData[0:])
   262  	afterMap := binary.BigEndian.Uint16(branchData[2:])
   263  	pos := 4
   264  	newData = append(newData, branchData[:4]...)
   265  	var accountI, storageI int
   266  	for bitset, j := touchMap&afterMap, 0; bitset != 0; j++ {
   267  		bit := bitset & -bitset
   268  		fieldBits := PartFlags(branchData[pos])
   269  		newData = append(newData, byte(fieldBits))
   270  		pos++
   271  		if fieldBits&HashedKeyPart != 0 {
   272  			l, n := binary.Uvarint(branchData[pos:])
   273  			if n == 0 {
   274  				return nil, fmt.Errorf("replacePlainKeys buffer too small for hashedKey len")
   275  			} else if n < 0 {
   276  				return nil, fmt.Errorf("replacePlainKeys value overflow for hashedKey len")
   277  			}
   278  			newData = append(newData, branchData[pos:pos+n]...)
   279  			pos += n
   280  			if len(branchData) < pos+int(l) {
   281  				return nil, fmt.Errorf("replacePlainKeys buffer too small for hashedKey")
   282  			}
   283  			if l > 0 {
   284  				newData = append(newData, branchData[pos:pos+int(l)]...)
   285  				pos += int(l)
   286  			}
   287  		}
   288  		if fieldBits&AccountPlainPart != 0 {
   289  			l, n := binary.Uvarint(branchData[pos:])
   290  			if n == 0 {
   291  				return nil, fmt.Errorf("replacePlainKeys buffer too small for accountPlainKey len")
   292  			} else if n < 0 {
   293  				return nil, fmt.Errorf("replacePlainKeys value overflow for accountPlainKey len")
   294  			}
   295  			pos += n
   296  			if len(branchData) < pos+int(l) {
   297  				return nil, fmt.Errorf("replacePlainKeys buffer too small for accountPlainKey")
   298  			}
   299  			if l > 0 {
   300  				pos += int(l)
   301  			}
   302  			n = binary.PutUvarint(numBuf[:], uint64(len(accountPlainKeys[accountI])))
   303  			newData = append(newData, numBuf[:n]...)
   304  			newData = append(newData, accountPlainKeys[accountI]...)
   305  			accountI++
   306  		}
   307  		if fieldBits&StoragePlainPart != 0 {
   308  			l, n := binary.Uvarint(branchData[pos:])
   309  			if n == 0 {
   310  				return nil, fmt.Errorf("replacePlainKeys buffer too small for storagePlainKey len")
   311  			} else if n < 0 {
   312  				return nil, fmt.Errorf("replacePlainKeys value overflow for storagePlainKey len")
   313  			}
   314  			pos += n
   315  			if len(branchData) < pos+int(l) {
   316  				return nil, fmt.Errorf("replacePlainKeys buffer too small for storagePlainKey")
   317  			}
   318  			if l > 0 {
   319  				pos += int(l)
   320  			}
   321  			n = binary.PutUvarint(numBuf[:], uint64(len(storagePlainKeys[storageI])))
   322  			newData = append(newData, numBuf[:n]...)
   323  			newData = append(newData, storagePlainKeys[storageI]...)
   324  			storageI++
   325  		}
   326  		if fieldBits&HashPart != 0 {
   327  			l, n := binary.Uvarint(branchData[pos:])
   328  			if n == 0 {
   329  				return nil, fmt.Errorf("replacePlainKeys buffer too small for hash len")
   330  			} else if n < 0 {
   331  				return nil, fmt.Errorf("replacePlainKeys value overflow for hash len")
   332  			}
   333  			newData = append(newData, branchData[pos:pos+n]...)
   334  			pos += n
   335  			if len(branchData) < pos+int(l) {
   336  				return nil, fmt.Errorf("replacePlainKeys buffer too small for hash")
   337  			}
   338  			if l > 0 {
   339  				newData = append(newData, branchData[pos:pos+int(l)]...)
   340  				pos += int(l)
   341  			}
   342  		}
   343  		bitset ^= bit
   344  	}
   345  	return newData, nil
   346  }
   347  
   348  // IsComplete determines whether given branch data is complete, meaning that all information about all the children is present
   349  // Each of 16 children of a branch node have two attributes
   350  // touch - whether this child has been modified or deleted in this branchData (corresponding bit in touchMap is set)
   351  // after - whether after this branchData application, the child is present in the tree or not (corresponding bit in afterMap is set)
   352  func (branchData BranchData) IsComplete() bool {
   353  	touchMap := binary.BigEndian.Uint16(branchData[0:])
   354  	afterMap := binary.BigEndian.Uint16(branchData[2:])
   355  	return ^touchMap&afterMap == 0
   356  }
   357  
   358  // MergeHexBranches combines two branchData, number 2 coming after (and potentially shadowing) number 1
   359  func (branchData BranchData) MergeHexBranches(branchData2 BranchData, newData []byte) (BranchData, error) {
   360  	if branchData2 == nil {
   361  		return branchData, nil
   362  	}
   363  	if branchData == nil {
   364  		return branchData2, nil
   365  	}
   366  
   367  	touchMap1 := binary.BigEndian.Uint16(branchData[0:])
   368  	afterMap1 := binary.BigEndian.Uint16(branchData[2:])
   369  	bitmap1 := touchMap1 & afterMap1
   370  	pos1 := 4
   371  	touchMap2 := binary.BigEndian.Uint16(branchData2[0:])
   372  	afterMap2 := binary.BigEndian.Uint16(branchData2[2:])
   373  	bitmap2 := touchMap2 & afterMap2
   374  	pos2 := 4
   375  	var bitmapBuf [4]byte
   376  	binary.BigEndian.PutUint16(bitmapBuf[0:], touchMap1|touchMap2)
   377  	binary.BigEndian.PutUint16(bitmapBuf[2:], afterMap2)
   378  	newData = append(newData, bitmapBuf[:]...)
   379  	for bitset, j := bitmap1|bitmap2, 0; bitset != 0; j++ {
   380  		bit := bitset & -bitset
   381  		if bitmap2&bit != 0 {
   382  			// Add fields from branchData2
   383  			fieldBits := PartFlags(branchData2[pos2])
   384  			newData = append(newData, byte(fieldBits))
   385  			pos2++
   386  			for i := 0; i < bits.OnesCount8(byte(fieldBits)); i++ {
   387  				l, n := binary.Uvarint(branchData2[pos2:])
   388  				if n == 0 {
   389  					return nil, fmt.Errorf("MergeHexBranches buffer2 too small for field")
   390  				} else if n < 0 {
   391  					return nil, fmt.Errorf("MergeHexBranches value2 overflow for field")
   392  				}
   393  				newData = append(newData, branchData2[pos2:pos2+n]...)
   394  				pos2 += n
   395  				if len(branchData2) < pos2+int(l) {
   396  					return nil, fmt.Errorf("MergeHexBranches buffer2 too small for field")
   397  				}
   398  				if l > 0 {
   399  					newData = append(newData, branchData2[pos2:pos2+int(l)]...)
   400  					pos2 += int(l)
   401  				}
   402  			}
   403  		}
   404  		if bitmap1&bit != 0 {
   405  			add := (touchMap2&bit == 0) && (afterMap2&bit != 0) // Add fields from branchData1
   406  			fieldBits := PartFlags(branchData[pos1])
   407  			if add {
   408  				newData = append(newData, byte(fieldBits))
   409  			}
   410  			pos1++
   411  			for i := 0; i < bits.OnesCount8(byte(fieldBits)); i++ {
   412  				l, n := binary.Uvarint(branchData[pos1:])
   413  				if n == 0 {
   414  					return nil, fmt.Errorf("MergeHexBranches buffer1 too small for field")
   415  				} else if n < 0 {
   416  					return nil, fmt.Errorf("MergeHexBranches value1 overflow for field")
   417  				}
   418  				if add {
   419  					newData = append(newData, branchData[pos1:pos1+n]...)
   420  				}
   421  				pos1 += n
   422  				if len(branchData) < pos1+int(l) {
   423  					return nil, fmt.Errorf("MergeHexBranches buffer1 too small for field")
   424  				}
   425  				if l > 0 {
   426  					if add {
   427  						newData = append(newData, branchData[pos1:pos1+int(l)]...)
   428  					}
   429  					pos1 += int(l)
   430  				}
   431  			}
   432  		}
   433  		bitset ^= bit
   434  	}
   435  	return newData, nil
   436  }
   437  
   438  func (branchData BranchData) DecodeCells() (touchMap, afterMap uint16, row [16]*Cell, err error) {
   439  	touchMap = binary.BigEndian.Uint16(branchData[0:])
   440  	afterMap = binary.BigEndian.Uint16(branchData[2:])
   441  	pos := 4
   442  	for bitset, j := touchMap, 0; bitset != 0; j++ {
   443  		bit := bitset & -bitset
   444  		nibble := bits.TrailingZeros16(bit)
   445  		if afterMap&bit != 0 {
   446  			fieldBits := PartFlags(branchData[pos])
   447  			pos++
   448  			row[nibble] = new(Cell)
   449  			if pos, err = row[nibble].fillFromFields(branchData, pos, fieldBits); err != nil {
   450  				err = fmt.Errorf("faield to fill cell at nibble %x: %w", nibble, err)
   451  				return
   452  			}
   453  		}
   454  		bitset ^= bit
   455  	}
   456  	return
   457  }
   458  
   459  type BranchMerger struct {
   460  	buf    *bytes.Buffer
   461  	num    [4]byte
   462  	keccak hash.Hash
   463  }
   464  
   465  func NewHexBranchMerger(capacity uint64) *BranchMerger {
   466  	return &BranchMerger{buf: bytes.NewBuffer(make([]byte, capacity)), keccak: sha3.NewLegacyKeccak256()}
   467  }
   468  
   469  // MergeHexBranches combines two branchData, number 2 coming after (and potentially shadowing) number 1
   470  func (m *BranchMerger) Merge(branch1 BranchData, branch2 BranchData) (BranchData, error) {
   471  	if branch2 == nil {
   472  		return branch1, nil
   473  	}
   474  	if branch1 == nil {
   475  		return branch2, nil
   476  	}
   477  
   478  	touchMap1 := binary.BigEndian.Uint16(branch1[0:])
   479  	afterMap1 := binary.BigEndian.Uint16(branch1[2:])
   480  	bitmap1 := touchMap1 & afterMap1
   481  	pos1 := 4
   482  
   483  	touchMap2 := binary.BigEndian.Uint16(branch2[0:])
   484  	afterMap2 := binary.BigEndian.Uint16(branch2[2:])
   485  	bitmap2 := touchMap2 & afterMap2
   486  	pos2 := 4
   487  
   488  	binary.BigEndian.PutUint16(m.num[0:], touchMap1|touchMap2)
   489  	binary.BigEndian.PutUint16(m.num[2:], afterMap2)
   490  	dataPos := 4
   491  
   492  	m.buf.Reset()
   493  	if _, err := m.buf.Write(m.num[:]); err != nil {
   494  		return nil, err
   495  	}
   496  
   497  	for bitset, j := bitmap1|bitmap2, 0; bitset != 0; j++ {
   498  		bit := bitset & -bitset
   499  		if bitmap2&bit != 0 {
   500  			// Add fields from branch2
   501  			fieldBits := PartFlags(branch2[pos2])
   502  			if err := m.buf.WriteByte(byte(fieldBits)); err != nil {
   503  				return nil, err
   504  			}
   505  			pos2++
   506  
   507  			for i := 0; i < bits.OnesCount8(byte(fieldBits)); i++ {
   508  				l, n := binary.Uvarint(branch2[pos2:])
   509  				if n == 0 {
   510  					return nil, fmt.Errorf("MergeHexBranches branch2 is too small: expected node info size")
   511  				} else if n < 0 {
   512  					return nil, fmt.Errorf("MergeHexBranches branch2: size overflow for length")
   513  				}
   514  
   515  				_, err := m.buf.Write(branch2[pos2 : pos2+n])
   516  				if err != nil {
   517  					return nil, err
   518  				}
   519  				pos2 += n
   520  				dataPos += n
   521  				if len(branch2) < pos2+int(l) {
   522  					return nil, fmt.Errorf("MergeHexBranches branch2 is too small: expected at least %d got %d bytes", pos2+int(l), len(branch2))
   523  				}
   524  				if l > 0 {
   525  					if _, err := m.buf.Write(branch2[pos2 : pos2+int(l)]); err != nil {
   526  						return nil, err
   527  					}
   528  					pos2 += int(l)
   529  					dataPos += int(l)
   530  				}
   531  			}
   532  		}
   533  		if bitmap1&bit != 0 {
   534  			add := (touchMap2&bit == 0) && (afterMap2&bit != 0) // Add fields from branchData1
   535  			fieldBits := PartFlags(branch1[pos1])
   536  			if add {
   537  				if err := m.buf.WriteByte(byte(fieldBits)); err != nil {
   538  					return nil, err
   539  				}
   540  			}
   541  			pos1++
   542  			for i := 0; i < bits.OnesCount8(byte(fieldBits)); i++ {
   543  				l, n := binary.Uvarint(branch1[pos1:])
   544  				if n == 0 {
   545  					return nil, fmt.Errorf("MergeHexBranches branch1 is too small: expected node info size")
   546  				} else if n < 0 {
   547  					return nil, fmt.Errorf("MergeHexBranches branch1: size overflow for length")
   548  				}
   549  				if add {
   550  					if _, err := m.buf.Write(branch1[pos1 : pos1+n]); err != nil {
   551  						return nil, err
   552  					}
   553  				}
   554  				pos1 += n
   555  				if len(branch1) < pos1+int(l) {
   556  					return nil, fmt.Errorf("MergeHexBranches branch1 is too small: expected at least %d got %d bytes", pos1+int(l), len(branch1))
   557  				}
   558  				if l > 0 {
   559  					if add {
   560  						if _, err := m.buf.Write(branch1[pos1 : pos1+int(l)]); err != nil {
   561  							return nil, err
   562  						}
   563  					}
   564  					pos1 += int(l)
   565  				}
   566  			}
   567  		}
   568  		bitset ^= bit
   569  	}
   570  	target := make([]byte, m.buf.Len())
   571  	copy(target, m.buf.Bytes())
   572  	return target, nil
   573  }
   574  
   575  func ParseTrieVariant(s string) TrieVariant {
   576  	var trieVariant TrieVariant
   577  	switch s {
   578  	case "bin":
   579  		trieVariant = VariantBinPatriciaTrie
   580  	case "hex":
   581  		fallthrough
   582  	default:
   583  		trieVariant = VariantHexPatriciaTrie
   584  	}
   585  	return trieVariant
   586  }