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

     1  //go:build !nofuzz
     2  
     3  package commitment
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"encoding/hex"
     9  	"math/rand"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"golang.org/x/crypto/sha3"
    14  
    15  	"github.com/ledgerwatch/erigon-lib/common/length"
    16  )
    17  
    18  // go test -trimpath -v -fuzz=Fuzz_ProcessUpdate$ -fuzztime=300s ./commitment
    19  
    20  func Fuzz_ProcessUpdate(f *testing.F) {
    21  	ha, _ := hex.DecodeString("13ccfe8074645cab4cb42b423625e055f0293c87")
    22  	hb, _ := hex.DecodeString("73f822e709a0016bfaed8b5e81b5f86de31d6895")
    23  
    24  	f.Add(uint64(2), ha, uint64(1235105), hb)
    25  
    26  	f.Fuzz(func(t *testing.T, balanceA uint64, accountA []byte, balanceB uint64, accountB []byte) {
    27  		if len(accountA) == 0 || len(accountA) > 20 || len(accountB) == 0 || len(accountB) > 20 {
    28  			t.Skip()
    29  		}
    30  
    31  		builder := NewUpdateBuilder().
    32  			Balance(hex.EncodeToString(accountA), balanceA).
    33  			Balance(hex.EncodeToString(accountB), balanceB)
    34  
    35  		ms := NewMockState(t)
    36  		ms2 := NewMockState(t)
    37  		hph := NewHexPatriciaHashed(20, ms.branchFn, ms.accountFn, ms.storageFn)
    38  		hphAnother := NewHexPatriciaHashed(20, ms2.branchFn, ms2.accountFn, ms2.storageFn)
    39  
    40  		hph.SetTrace(false)
    41  		hphAnother.SetTrace(false)
    42  
    43  		plainKeys, hashedKeys, updates := builder.Build()
    44  		if err := ms.applyPlainUpdates(plainKeys, updates); err != nil {
    45  			t.Fatal(err)
    46  		}
    47  		if err := ms2.applyPlainUpdates(plainKeys, updates); err != nil {
    48  			t.Fatal(err)
    49  		}
    50  
    51  		rootHash, branchNodeUpdates, err := hph.ReviewKeys(plainKeys, hashedKeys)
    52  		if err != nil {
    53  			t.Fatal(err)
    54  		}
    55  
    56  		ms.applyBranchNodeUpdates(branchNodeUpdates)
    57  		if len(rootHash) != 32 {
    58  			t.Fatalf("invalid root hash length: expected 32 bytes, got %v", len(rootHash))
    59  		}
    60  
    61  		rootHashAnother, branchNodeUpdates, err := hphAnother.ReviewKeys(plainKeys, hashedKeys)
    62  		if err != nil {
    63  			t.Fatal(err)
    64  		}
    65  		ms2.applyBranchNodeUpdates(branchNodeUpdates)
    66  
    67  		if len(rootHashAnother) > 32 {
    68  			t.Fatalf("invalid root hash length: expected 32 bytes, got %v", len(rootHash))
    69  		}
    70  		if !bytes.Equal(rootHash, rootHashAnother) {
    71  			t.Fatalf("invalid second root hash with same updates: [%v] != [%v]", hex.EncodeToString(rootHash), hex.EncodeToString(rootHashAnother))
    72  		}
    73  	})
    74  }
    75  
    76  // go test -trimpath -v -fuzz=Fuzz_ProcessUpdates_ArbitraryUpdateCount -fuzztime=300s ./commitment
    77  
    78  func Fuzz_ProcessUpdates_ArbitraryUpdateCount(f *testing.F) {
    79  	ha, _ := hex.DecodeString("0008852883b2850c7a48f4b0eea3ccc4c04e6cb6025e9e8f7db2589c7dae81517c514790cfd6f668903161349e")
    80  
    81  	f.Add(ha)
    82  
    83  	f.Fuzz(func(t *testing.T, build []byte) {
    84  		if len(build) < 12 {
    85  			t.Skip()
    86  		}
    87  		i := 0
    88  		keysCount := binary.BigEndian.Uint32(build[i : i+4])
    89  		i += 4
    90  		ks := binary.BigEndian.Uint32(build[i : i+4])
    91  		keysSeed := rand.New(rand.NewSource(int64(ks)))
    92  		i += 4
    93  		us := binary.BigEndian.Uint32(build[i : i+4])
    94  		updateSeed := rand.New(rand.NewSource(int64(us)))
    95  
    96  		t.Logf("fuzzing %d keys keysSeed=%d updateSeed=%d", keysCount, ks, us)
    97  
    98  		builder := NewUpdateBuilder()
    99  		for k := uint32(0); k < keysCount; k++ {
   100  			var key [length.Addr]byte
   101  			n, err := keysSeed.Read(key[:])
   102  			pkey := hex.EncodeToString(key[:])
   103  			require.NoError(t, err)
   104  			require.EqualValues(t, length.Addr, n)
   105  
   106  			aux := make([]byte, 32)
   107  
   108  			flg := UpdateFlags(updateSeed.Intn(int(CodeUpdate | DeleteUpdate | StorageUpdate | NonceUpdate | BalanceUpdate)))
   109  			switch {
   110  			case flg&BalanceUpdate != 0:
   111  				builder.Balance(pkey, updateSeed.Uint64()).Nonce(pkey, updateSeed.Uint64())
   112  				continue
   113  			case flg&CodeUpdate != 0:
   114  				keccak := sha3.NewLegacyKeccak256().(keccakState)
   115  				var s [8]byte
   116  				n, err := updateSeed.Read(s[:])
   117  				require.NoError(t, err)
   118  				require.EqualValues(t, len(s), n)
   119  				keccak.Write(s[:])
   120  				keccak.Read(aux)
   121  
   122  				builder.CodeHash(pkey, hex.EncodeToString(aux))
   123  				continue
   124  			case flg&StorageUpdate != 0:
   125  				sz := updateSeed.Intn(length.Hash)
   126  				n, err = updateSeed.Read(aux[:sz])
   127  				require.NoError(t, err)
   128  				require.EqualValues(t, sz, n)
   129  
   130  				loc := make([]byte, updateSeed.Intn(length.Hash-1)+1)
   131  				keysSeed.Read(loc)
   132  				builder.Storage(pkey, hex.EncodeToString(loc), hex.EncodeToString(aux[:sz]))
   133  				continue
   134  			case flg&DeleteUpdate != 0:
   135  				continue
   136  			default:
   137  				continue
   138  			}
   139  		}
   140  
   141  		ms := NewMockState(t)
   142  		ms2 := NewMockState(t)
   143  		hph := NewHexPatriciaHashed(20, ms.branchFn, ms.accountFn, ms.storageFn)
   144  		hphAnother := NewHexPatriciaHashed(20, ms2.branchFn, ms2.accountFn, ms2.storageFn)
   145  
   146  		plainKeys, hashedKeys, updates := builder.Build()
   147  
   148  		hph.SetTrace(false)
   149  		hphAnother.SetTrace(false)
   150  
   151  		err := ms.applyPlainUpdates(plainKeys, updates)
   152  		require.NoError(t, err)
   153  
   154  		rootHashReview, branchNodeUpdates, err := hph.ReviewKeys(plainKeys, hashedKeys)
   155  		require.NoError(t, err)
   156  
   157  		ms.applyBranchNodeUpdates(branchNodeUpdates)
   158  		require.Len(t, rootHashReview, length.Hash, "invalid root hash length")
   159  
   160  		err = ms2.applyPlainUpdates(plainKeys, updates)
   161  		require.NoError(t, err)
   162  
   163  		rootHashAnother, branchUpdatesAnother, err := hphAnother.ReviewKeys(plainKeys, hashedKeys)
   164  		require.NoError(t, err)
   165  		ms2.applyBranchNodeUpdates(branchUpdatesAnother)
   166  
   167  		require.Len(t, rootHashAnother, length.Hash, "invalid root hash length")
   168  		require.EqualValues(t, rootHashReview, rootHashAnother, "storage-based and update-based rootHash mismatch")
   169  	})
   170  }
   171  
   172  func Fuzz_HexPatriciaHashed_ReviewKeys(f *testing.F) {
   173  	var (
   174  		keysCount uint64 = 100
   175  		seed      int64  = 1234123415
   176  	)
   177  
   178  	f.Add(keysCount, seed)
   179  
   180  	f.Fuzz(func(t *testing.T, keysCount uint64, seed int64) {
   181  		if keysCount > 10e9 {
   182  			return
   183  		}
   184  
   185  		rnd := rand.New(rand.NewSource(seed))
   186  		builder := NewUpdateBuilder()
   187  
   188  		// generate updates
   189  		for i := 0; i < int(keysCount); i++ {
   190  			key := make([]byte, length.Addr)
   191  
   192  			for j := 0; j < len(key); j++ {
   193  				key[j] = byte(rnd.Intn(256))
   194  			}
   195  			builder.Balance(hex.EncodeToString(key), rnd.Uint64())
   196  		}
   197  
   198  		ms := NewMockState(t)
   199  		hph := NewHexPatriciaHashed(length.Addr, ms.branchFn, ms.accountFn, ms.storageFn)
   200  
   201  		hph.SetTrace(false)
   202  
   203  		plainKeys, hashedKeys, updates := builder.Build()
   204  		if err := ms.applyPlainUpdates(plainKeys, updates); err != nil {
   205  			t.Fatal(err)
   206  		}
   207  
   208  		rootHash, branchNodeUpdates, err := hph.ReviewKeys(plainKeys, hashedKeys)
   209  		require.NoError(t, err)
   210  
   211  		ms.applyBranchNodeUpdates(branchNodeUpdates)
   212  		require.Lenf(t, rootHash, length.Hash, "invalid root hash length")
   213  	})
   214  }