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