From e833d333fe7fabac9477f41d412d68366df880c7 Mon Sep 17 00:00:00 2001
From: Evgeniy Stratonikov <evgeniy@nspcc.ru>
Date: Thu, 3 Jun 2021 15:46:35 +0300
Subject: [PATCH] mpt: disallow empty keys

This is not a problem in practice, as all keys are prefixed
by a contract ID. However in theory it can lead to a different
state root after new portion of changes thus this fix.
---
 pkg/core/mpt/batch_test.go | 18 +++++++++---------
 pkg/core/mpt/trie.go       |  4 +++-
 pkg/core/mpt/trie_test.go  |  9 ++++-----
 3 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/pkg/core/mpt/batch_test.go b/pkg/core/mpt/batch_test.go
index 9438b8e2b..e8233ee82 100644
--- a/pkg/core/mpt/batch_test.go
+++ b/pkg/core/mpt/batch_test.go
@@ -70,26 +70,26 @@ func TestTrie_PutBatchLeaf(t *testing.T) {
 	prepareLeaf := func(t *testing.T) (*Trie, *Trie) {
 		tr1 := NewTrie(new(HashNode), false, newTestStore())
 		tr2 := NewTrie(new(HashNode), false, newTestStore())
-		require.NoError(t, tr1.Put([]byte{}, []byte("value")))
-		require.NoError(t, tr2.Put([]byte{}, []byte("value")))
+		require.NoError(t, tr1.Put([]byte{0}, []byte("value")))
+		require.NoError(t, tr2.Put([]byte{0}, []byte("value")))
 		return tr1, tr2
 	}
 
 	t.Run("remove", func(t *testing.T) {
 		tr1, tr2 := prepareLeaf(t)
-		var ps = pairs{{[]byte{}, nil}}
+		var ps = pairs{{[]byte{0}, nil}}
 		testPut(t, ps, tr1, tr2)
 	})
 	t.Run("replace", func(t *testing.T) {
 		tr1, tr2 := prepareLeaf(t)
-		var ps = pairs{{[]byte{}, []byte("replace")}}
+		var ps = pairs{{[]byte{0}, []byte("replace")}}
 		testPut(t, ps, tr1, tr2)
 	})
 	t.Run("remove and replace", func(t *testing.T) {
 		tr1, tr2 := prepareLeaf(t)
 		var ps = pairs{
-			{[]byte{}, nil},
-			{[]byte{2}, []byte("replace2")},
+			{[]byte{0}, nil},
+			{[]byte{0, 2}, []byte("replace2")},
 		}
 		testPut(t, ps, tr1, tr2)
 	})
@@ -122,7 +122,7 @@ func TestTrie_PutBatchExtension(t *testing.T) {
 	t.Run("add to next with leaf", func(t *testing.T) {
 		tr1, tr2 := prepareExtension(t)
 		var ps = pairs{
-			{[]byte{}, []byte("value3")},
+			{[]byte{0}, []byte("value3")},
 			{[]byte{1, 2, 3}, []byte("value2")},
 		}
 		testPut(t, ps, tr1, tr2)
@@ -232,7 +232,7 @@ func TestTrie_PutBatchEmpty(t *testing.T) {
 		tr1 := NewTrie(new(HashNode), false, newTestStore())
 		tr2 := NewTrie(new(HashNode), false, newTestStore())
 		var ps = pairs{
-			{[]byte{}, []byte("value0")},
+			{[]byte{0}, []byte("value0")},
 			{[]byte{1}, []byte("value1")},
 			{[]byte{3}, []byte("value3")},
 		}
@@ -240,7 +240,7 @@ func TestTrie_PutBatchEmpty(t *testing.T) {
 	})
 	t.Run("incomplete", func(t *testing.T) {
 		var ps = pairs{
-			{[]byte{}, []byte("replace0")},
+			{[]byte{0}, []byte("replace0")},
 			{[]byte{1}, []byte("replace1")},
 			{[]byte{2}, nil},
 			{[]byte{3}, []byte("replace3")},
diff --git a/pkg/core/mpt/trie.go b/pkg/core/mpt/trie.go
index 6a0335273..6f4b452a4 100644
--- a/pkg/core/mpt/trie.go
+++ b/pkg/core/mpt/trie.go
@@ -97,7 +97,9 @@ func (t *Trie) getWithPath(curr Node, path []byte) (Node, []byte, error) {
 
 // Put puts key-value pair in t.
 func (t *Trie) Put(key, value []byte) error {
-	if len(key) > MaxKeyLength {
+	if len(key) == 0 {
+		return errors.New("key is empty")
+	} else if len(key) > MaxKeyLength {
 		return errors.New("key is too big")
 	} else if len(value) > MaxValueLength {
 		return errors.New("value is too big")
diff --git a/pkg/core/mpt/trie_test.go b/pkg/core/mpt/trie_test.go
index 405c501dc..18d0d43b6 100644
--- a/pkg/core/mpt/trie_test.go
+++ b/pkg/core/mpt/trie_test.go
@@ -85,10 +85,6 @@ func TestTrie_PutIntoBranchNode(t *testing.T) {
 	b.Children[0x8] = NewHashNode(random.Uint256())
 	tr := NewTrie(b, false, newTestStore())
 
-	// next
-	require.NoError(t, tr.Put([]byte{}, []byte{0x12, 0x34}))
-	tr.testHas(t, []byte{}, []byte{0x12, 0x34})
-
 	// empty hash node child
 	require.NoError(t, tr.Put([]byte{0x66}, []byte{0x56}))
 	tr.testHas(t, []byte{0x66}, []byte{0x56})
@@ -160,6 +156,9 @@ func TestTrie_PutInvalid(t *testing.T) {
 	tr := NewTrie(nil, false, newTestStore())
 	key, value := []byte("key"), []byte("value")
 
+	// empty key
+	require.Error(t, tr.Put(nil, value))
+
 	// big key
 	require.Error(t, tr.Put(make([]byte, maxPathLength+1), value))
 
@@ -271,7 +270,7 @@ func TestTrie_Get(t *testing.T) {
 
 func TestTrie_Flush(t *testing.T) {
 	pairs := map[string][]byte{
-		"":     []byte("value0"),
+		"x":    []byte("value0"),
 		"key1": []byte("value1"),
 		"key2": []byte("value2"),
 	}