Skip to content

Commit ad0a59a

Browse files
committed
Add predicate packing helper
1 parent 9a1c548 commit ad0a59a

File tree

4 files changed

+141
-54
lines changed

4 files changed

+141
-54
lines changed

utils/bytes.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
package utils
55

6-
import "github.com/ethereum/go-ethereum/common"
7-
86
// IncrOne increments bytes value by one
97
func IncrOne(bytes []byte) {
108
index := len(bytes) - 1
@@ -18,27 +16,3 @@ func IncrOne(bytes []byte) {
1816
}
1917
}
2018
}
21-
22-
// HashSliceToBytes serializes a []common.Hash into a tightly packed byte array.
23-
func HashSliceToBytes(hashes []common.Hash) []byte {
24-
bytes := make([]byte, common.HashLength*len(hashes))
25-
for i, hash := range hashes {
26-
copy(bytes[i*common.HashLength:], hash[:])
27-
}
28-
return bytes
29-
}
30-
31-
// BytesToHashSlice packs [b] into a slice of hash values with zero padding
32-
// to the right if the length of b is not a multiple of 32.
33-
func BytesToHashSlice(b []byte) []common.Hash {
34-
var (
35-
numHashes = (len(b) + 31) / 32
36-
hashes = make([]common.Hash, numHashes)
37-
)
38-
39-
for i := range hashes {
40-
start := i * common.HashLength
41-
copy(hashes[i][:], b[start:])
42-
}
43-
return hashes
44-
}

utils/bytes_test.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
package utils
55

66
import (
7-
"bytes"
87
"testing"
98

10-
"github.com/ava-labs/avalanchego/utils"
119
"github.com/ethereum/go-ethereum/common"
1210
"github.com/stretchr/testify/assert"
13-
"github.com/stretchr/testify/require"
1411
)
1512

1613
func TestIncrOne(t *testing.T) {
@@ -39,28 +36,3 @@ func TestIncrOne(t *testing.T) {
3936
})
4037
}
4138
}
42-
43-
func testBytesToHashSlice(t testing.TB, b []byte) {
44-
hashSlice := BytesToHashSlice(b)
45-
46-
copiedBytes := HashSliceToBytes(hashSlice)
47-
48-
if len(b)%32 == 0 {
49-
require.Equal(t, b, copiedBytes)
50-
} else {
51-
require.Equal(t, b, copiedBytes[:len(b)])
52-
// Require that any additional padding is all zeroes
53-
padding := copiedBytes[len(b):]
54-
require.Equal(t, bytes.Repeat([]byte{0x00}, len(padding)), padding)
55-
}
56-
}
57-
58-
func FuzzHashSliceToBytes(f *testing.F) {
59-
for i := 0; i < 100; i++ {
60-
f.Add(utils.RandomBytes(i))
61-
}
62-
63-
f.Fuzz(func(t *testing.T, a []byte) {
64-
testBytesToHashSlice(t, a)
65-
})
66-
}

utils/predicate_bytes.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// (c) 2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package utils
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/ethereum/go-ethereum/common"
10+
)
11+
12+
// PredicateEndByte is used as a delimiter for the bytes packed into a precompile predicate.
13+
// Precompile predicates are encoded in the Access List of transactions in the access tuples
14+
// which means that its length must be a multiple of 32 (common.HashLength).
15+
// For messages with a length that does not comply to that, this delimiter is used to
16+
// append/remove padding.
17+
var PredicateEndByte = byte(0xff)
18+
19+
// PackPredicate packs [predicate] by delimiting the actual message with [PredicateEndByte]
20+
// and zero padding to reach a length that is a multiple of 32.
21+
func PackPredicate(predicate []byte) []byte {
22+
predicate = append(predicate, PredicateEndByte)
23+
return common.RightPadBytes(predicate, (len(predicate)+31)/32*32)
24+
}
25+
26+
// UnpackPredicate unpacks a predicate by stripping right padded zeroes, checking for the delimter,
27+
// ensuring there is not excess padding, and returning the original message.
28+
// Returns an error if it finds an incorrect encoding.
29+
func UnpackPredicate(paddedPredicate []byte) ([]byte, error) {
30+
trimmedPredicateBytes := common.TrimRightZeroes(paddedPredicate)
31+
if len(trimmedPredicateBytes) == 0 {
32+
return nil, fmt.Errorf("predicate specified invalid all zero bytes: 0x%x", paddedPredicate)
33+
}
34+
35+
if expectedPaddedLength := (len(trimmedPredicateBytes) + 31) / 32 * 32; expectedPaddedLength != len(paddedPredicate) {
36+
return nil, fmt.Errorf("predicate specified invalid padding with length (%d), expected length (%d)", len(paddedPredicate), expectedPaddedLength)
37+
}
38+
39+
if trimmedPredicateBytes[len(trimmedPredicateBytes)-1] != PredicateEndByte {
40+
return nil, fmt.Errorf("invalid end delimiter")
41+
}
42+
43+
return trimmedPredicateBytes[:len(trimmedPredicateBytes)-1], nil
44+
}
45+
46+
// HashSliceToBytes serializes a []common.Hash into a tightly packed byte array.
47+
func HashSliceToBytes(hashes []common.Hash) []byte {
48+
bytes := make([]byte, common.HashLength*len(hashes))
49+
for i, hash := range hashes {
50+
copy(bytes[i*common.HashLength:], hash[:])
51+
}
52+
return bytes
53+
}
54+
55+
// BytesToHashSlice packs [b] into a slice of hash values with zero padding
56+
// to the right if the length of b is not a multiple of 32.
57+
func BytesToHashSlice(b []byte) []common.Hash {
58+
var (
59+
numHashes = (len(b) + 31) / 32
60+
hashes = make([]common.Hash, numHashes)
61+
)
62+
63+
for i := range hashes {
64+
start := i * common.HashLength
65+
copy(hashes[i][:], b[start:])
66+
}
67+
return hashes
68+
}

utils/predicate_bytes_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// (c) 2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package utils
5+
6+
import (
7+
"bytes"
8+
"testing"
9+
10+
"github.com/ava-labs/avalanchego/utils"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func testBytesToHashSlice(t testing.TB, b []byte) {
15+
hashSlice := BytesToHashSlice(b)
16+
17+
copiedBytes := HashSliceToBytes(hashSlice)
18+
19+
if len(b)%32 == 0 {
20+
require.Equal(t, b, copiedBytes)
21+
} else {
22+
require.Equal(t, b, copiedBytes[:len(b)])
23+
// Require that any additional padding is all zeroes
24+
padding := copiedBytes[len(b):]
25+
require.Equal(t, bytes.Repeat([]byte{0x00}, len(padding)), padding)
26+
}
27+
}
28+
29+
func FuzzHashSliceToBytes(f *testing.F) {
30+
for i := 0; i < 100; i++ {
31+
f.Add(utils.RandomBytes(i))
32+
}
33+
34+
f.Fuzz(func(t *testing.T, b []byte) {
35+
testBytesToHashSlice(t, b)
36+
})
37+
}
38+
39+
func testPackPredicate(t testing.TB, b []byte) {
40+
packedPredicate := PackPredicate(b)
41+
unpackedPredicated, err := UnpackPredicate(packedPredicate)
42+
require.NoError(t, err)
43+
require.Equal(t, b, unpackedPredicated)
44+
}
45+
46+
func FuzzPackPredicate(f *testing.F) {
47+
for i := 0; i < 100; i++ {
48+
f.Add(utils.RandomBytes(i))
49+
}
50+
51+
f.Fuzz(func(t *testing.T, b []byte) {
52+
testPackPredicate(t, b)
53+
})
54+
}
55+
56+
func FuzzUnpackInvalidPredicate(f *testing.F) {
57+
// Seed the fuzzer with non-zero length padding of zeroes or non-zeroes.
58+
for i := 1; i < 100; i++ {
59+
f.Add(utils.RandomBytes(i))
60+
f.Add(make([]byte, i))
61+
}
62+
63+
f.Fuzz(func(t *testing.T, b []byte) {
64+
// Ensure that adding the invalid padding to any length correctly packed predicate
65+
// results in failing to unpack it.
66+
for _, l := range []int{0, 1, 31, 32, 33, 63, 64, 65} {
67+
validPredicate := PackPredicate(utils.RandomBytes(l))
68+
invalidPredicate := append(validPredicate, b...)
69+
_, err := UnpackPredicate(invalidPredicate)
70+
require.Error(t, err)
71+
}
72+
})
73+
}

0 commit comments

Comments
 (0)