diff --git a/contracts/wasm/corecontracts/test/core_blob_test.go b/contracts/wasm/corecontracts/test/core_blob_test.go index 2f0f0238ce..03688cb365 100644 --- a/contracts/wasm/corecontracts/test/core_blob_test.go +++ b/contracts/wasm/corecontracts/test/core_blob_test.go @@ -14,7 +14,7 @@ import ( ) // this is the expected blob hash for key0/val0 key1/val1 -const expectedBlobHash = "0x5fec3bfc701d80bdf75e337cb3dcb401c2423d15fc17a74d5b644dae143118b1" +const expectedBlobHash = "0x54cb8e9c45ca6d368dba92da34cfa47ce617f04807af19f67de333fad0039e6b" func setupBlob(t *testing.T) *wasmsolo.SoloContext { ctx := setup(t) @@ -78,15 +78,14 @@ func TestListBlobs(t *testing.T) { fStore := coreblob.ScFuncs.StoreBlob(ctx) fStore.Params.Blobs().GetBytes("key0").SetValue([]byte("val0")) - fStore.Params.Blobs().GetBytes("key1").SetValue([]byte("_val1")) + fStore.Params.Blobs().GetBytes("key1").SetValue([]byte("val1")) fStore.Func.Post() require.NoError(t, ctx.Err) - expectedHash := "0x462af4abe5977f4dd985a0a097705925b9fa6c033c9d931c1e2171f710693462" - require.Equal(t, expectedHash, fStore.Results.Hash().Value().String()) + require.Equal(t, expectedBlobHash, fStore.Results.Hash().Value().String()) fList := coreblob.ScFuncs.ListBlobs(ctx) fList.Func.Call() - size := fList.Results.BlobSizes().GetInt32(wasmtypes.HashFromString(expectedHash)).Value() - // The sum of the size of the value of `key0` and `key1` is len("val0")+len("_val1") = 9 - require.Equal(t, int32(9), size) + size := fList.Results.BlobSizes().GetInt32(wasmtypes.HashFromString(expectedBlobHash)).Value() + // The sum of the size of the value of `key0` and `key1` is len("val0")+len("val1") = 8 + require.Equal(t, int32(8), size) } diff --git a/packages/vm/core/blob/internal.go b/packages/vm/core/blob/internal.go index 4baca537cd..ab3fe46483 100644 --- a/packages/vm/core/blob/internal.go +++ b/packages/vm/core/blob/internal.go @@ -1,6 +1,7 @@ package blob import ( + "encoding/binary" "fmt" "github.com/iotaledger/wasp/packages/hashing" @@ -25,11 +26,15 @@ func mustGetBlobHash(fields dict.Dict) (hashing.HashValue, []kv.Key, [][]byte) { sorted := fields.KeysSorted() // mind determinism values := make([][]byte, 0, len(sorted)) all := make([][]byte, 0, 2*len(sorted)) - for _, k := range sorted { - v := fields.Get(k) + var prefix [4]byte + + // hashBlob = hash(KeyLen0|Key0|Val0 | KeyLen1|Key1|Val1 | ... | KeyLenN|KeyN|ValN) + // by prepend the key length we can avoid the possible collision + for _, key := range sorted { + v := fields.Get(key) values = append(values, v) - all = append(all, v) - all = append(all, []byte(k)) + binary.LittleEndian.PutUint32(prefix[:], uint32(len(key))) + all = append(all, prefix[:], []byte(key), v) } return hashing.HashData(all...), sorted, values } diff --git a/packages/vm/core/blob/internal_test.go b/packages/vm/core/blob/internal_test.go new file mode 100644 index 0000000000..3fb1184caf --- /dev/null +++ b/packages/vm/core/blob/internal_test.go @@ -0,0 +1,43 @@ +package blob + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/wasp/packages/kv/dict" +) + +func TestMustGetBlobHash(t *testing.T) { + t.Run("normal", func(t *testing.T) { + fields := dict.Dict{ + "key0": []byte("val0"), + "key1": []byte("val1"), + } + + h, keys, values := mustGetBlobHash(fields) + for i, k := range keys { + require.Equal(t, fields[k], values[i]) + } + + resHash, err := hex.DecodeString("54cb8e9c45ca6d368dba92da34cfa47ce617f04807af19f67de333fad0039e6b") + require.NoError(t, err) + require.Equal(t, resHash, h.Bytes()) + }) + t.Run("potential collision", func(t *testing.T) { + fields := dict.Dict{ + "123": []byte("ab"), + "123a": []byte("b"), + } + + h, keys, values := mustGetBlobHash(fields) + for i, k := range keys { + require.Equal(t, fields[k], values[i]) + } + + resHash, err := hex.DecodeString("5a99cff5dcba5d8b0efcbdc8b534a57853fd468d03109d941937e480b37361b1") + require.NoError(t, err) + require.Equal(t, resHash, h.Bytes()) + }) +}