github.com/MetalBlockchain/subnet-evm@v0.6.3/tests/rlp_test_util.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2015 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package tests 28 29 import ( 30 "bytes" 31 "encoding/hex" 32 "errors" 33 "fmt" 34 "math/big" 35 "strings" 36 37 "github.com/ethereum/go-ethereum/rlp" 38 ) 39 40 // RLPTest is the JSON structure of a single RLP test. 41 type RLPTest struct { 42 // If the value of In is "INVALID" or "VALID", the test 43 // checks whether Out can be decoded into a value of 44 // type interface{}. 45 // 46 // For other JSON values, In is treated as a driver for 47 // calls to rlp.Stream. The test also verifies that encoding 48 // In produces the bytes in Out. 49 In interface{} 50 51 // Out is a hex-encoded RLP value. 52 Out string 53 } 54 55 // FromHex returns the bytes represented by the hexadecimal string s. 56 // s may be prefixed with "0x". 57 // This is copy-pasted from bytes.go, which does not return the error 58 func FromHex(s string) ([]byte, error) { 59 if len(s) > 1 && (s[0:2] == "0x" || s[0:2] == "0X") { 60 s = s[2:] 61 } 62 if len(s)%2 == 1 { 63 s = "0" + s 64 } 65 return hex.DecodeString(s) 66 } 67 68 // Run executes the test. 69 func (t *RLPTest) Run() error { 70 outb, err := FromHex(t.Out) 71 if err != nil { 72 return errors.New("invalid hex in Out") 73 } 74 75 // Handle simple decoding tests with no actual In value. 76 if t.In == "VALID" || t.In == "INVALID" { 77 return checkDecodeInterface(outb, t.In == "VALID") 78 } 79 80 // Check whether encoding the value produces the same bytes. 81 in := translateJSON(t.In) 82 b, err := rlp.EncodeToBytes(in) 83 if err != nil { 84 return fmt.Errorf("encode failed: %v", err) 85 } 86 if !bytes.Equal(b, outb) { 87 return fmt.Errorf("encode produced %x, want %x", b, outb) 88 } 89 // Test stream decoding. 90 s := rlp.NewStream(bytes.NewReader(outb), 0) 91 return checkDecodeFromJSON(s, in) 92 } 93 94 func checkDecodeInterface(b []byte, isValid bool) error { 95 err := rlp.DecodeBytes(b, new(interface{})) 96 switch { 97 case isValid && err != nil: 98 return fmt.Errorf("decoding failed: %v", err) 99 case !isValid && err == nil: 100 return errors.New("decoding of invalid value succeeded") 101 } 102 return nil 103 } 104 105 // translateJSON makes test json values encodable with RLP. 106 func translateJSON(v interface{}) interface{} { 107 switch v := v.(type) { 108 case float64: 109 return uint64(v) 110 case string: 111 if len(v) > 0 && v[0] == '#' { // # starts a faux big int. 112 big, ok := new(big.Int).SetString(v[1:], 10) 113 if !ok { 114 panic(fmt.Errorf("bad test: bad big int: %q", v)) 115 } 116 return big 117 } 118 return []byte(v) 119 case []interface{}: 120 new := make([]interface{}, len(v)) 121 for i := range v { 122 new[i] = translateJSON(v[i]) 123 } 124 return new 125 default: 126 panic(fmt.Errorf("can't handle %T", v)) 127 } 128 } 129 130 // checkDecodeFromJSON decodes from s guided by exp. exp drives the 131 // Stream by invoking decoding operations (Uint, Big, List, ...) based 132 // on the type of each value. The value decoded from the RLP stream 133 // must match the JSON value. 134 func checkDecodeFromJSON(s *rlp.Stream, exp interface{}) error { 135 switch exp := exp.(type) { 136 case uint64: 137 i, err := s.Uint64() 138 if err != nil { 139 return addStack("Uint", exp, err) 140 } 141 if i != exp { 142 return addStack("Uint", exp, fmt.Errorf("result mismatch: got %d", i)) 143 } 144 case *big.Int: 145 big := new(big.Int) 146 if err := s.Decode(&big); err != nil { 147 return addStack("Big", exp, err) 148 } 149 if big.Cmp(exp) != 0 { 150 return addStack("Big", exp, fmt.Errorf("result mismatch: got %d", big)) 151 } 152 case []byte: 153 b, err := s.Bytes() 154 if err != nil { 155 return addStack("Bytes", exp, err) 156 } 157 if !bytes.Equal(b, exp) { 158 return addStack("Bytes", exp, fmt.Errorf("result mismatch: got %x", b)) 159 } 160 case []interface{}: 161 if _, err := s.List(); err != nil { 162 return addStack("List", exp, err) 163 } 164 for i, v := range exp { 165 if err := checkDecodeFromJSON(s, v); err != nil { 166 return addStack(fmt.Sprintf("[%d]", i), exp, err) 167 } 168 } 169 if err := s.ListEnd(); err != nil { 170 return addStack("ListEnd", exp, err) 171 } 172 default: 173 panic(fmt.Errorf("unhandled type: %T", exp)) 174 } 175 return nil 176 } 177 178 func addStack(op string, val interface{}, err error) error { 179 lines := strings.Split(err.Error(), "\n") 180 lines = append(lines, fmt.Sprintf("\t%s: %v", op, val)) 181 return errors.New(strings.Join(lines, "\n")) 182 }