|
| 1 | +// (c) 2023, Ava Labs, Inc. All rights reserved. |
| 2 | +// See the file LICENSE for licensing terms. |
| 3 | + |
| 4 | +package predicateutils |
| 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 | +} |
0 commit comments