github.com/clem109/go-ethereum@v1.8.3-0.20180316121352-fe6cf00f480a/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 "math/big" 25 "strings" 26 27 "github.com/ethereum/go-ethereum/rlp" 28 ) 29 30 // RLPTest is the JSON structure of a single RLP test. 31 type RLPTest struct { 32 // If the value of In is "INVALID" or "VALID", the test 33 // checks whether Out can be decoded into a value of 34 // type interface{}. 35 // 36 // For other JSON values, In is treated as a driver for 37 // calls to rlp.Stream. The test also verifies that encoding 38 // In produces the bytes in Out. 39 In interface{} 40 41 // Out is a hex-encoded RLP value. 42 Out string 43 } 44 45 // Run executes the test. 46 func (t *RLPTest) Run() error { 47 outb, err := hex.DecodeString(t.Out) 48 if err != nil { 49 return fmt.Errorf("invalid hex in Out") 50 } 51 52 // Handle simple decoding tests with no actual In value. 53 if t.In == "VALID" || t.In == "INVALID" { 54 return checkDecodeInterface(outb, t.In == "VALID") 55 } 56 57 // Check whether encoding the value produces the same bytes. 58 in := translateJSON(t.In) 59 b, err := rlp.EncodeToBytes(in) 60 if err != nil { 61 return fmt.Errorf("encode failed: %v", err) 62 } 63 if !bytes.Equal(b, outb) { 64 return fmt.Errorf("encode produced %x, want %x", b, outb) 65 } 66 // Test stream decoding. 67 s := rlp.NewStream(bytes.NewReader(outb), 0) 68 return checkDecodeFromJSON(s, in) 69 } 70 71 func checkDecodeInterface(b []byte, isValid bool) error { 72 err := rlp.DecodeBytes(b, new(interface{})) 73 switch { 74 case isValid && err != nil: 75 return fmt.Errorf("decoding failed: %v", err) 76 case !isValid && err == nil: 77 return fmt.Errorf("decoding of invalid value succeeded") 78 } 79 return nil 80 } 81 82 // translateJSON makes test json values encodable with RLP. 83 func translateJSON(v interface{}) interface{} { 84 switch v := v.(type) { 85 case float64: 86 return uint64(v) 87 case string: 88 if len(v) > 0 && v[0] == '#' { // # starts a faux big int. 89 big, ok := new(big.Int).SetString(v[1:], 10) 90 if !ok { 91 panic(fmt.Errorf("bad test: bad big int: %q", v)) 92 } 93 return big 94 } 95 return []byte(v) 96 case []interface{}: 97 new := make([]interface{}, len(v)) 98 for i := range v { 99 new[i] = translateJSON(v[i]) 100 } 101 return new 102 default: 103 panic(fmt.Errorf("can't handle %T", v)) 104 } 105 } 106 107 // checkDecodeFromJSON decodes from s guided by exp. exp drives the 108 // Stream by invoking decoding operations (Uint, Big, List, ...) based 109 // on the type of each value. The value decoded from the RLP stream 110 // must match the JSON value. 111 func checkDecodeFromJSON(s *rlp.Stream, exp interface{}) error { 112 switch exp := exp.(type) { 113 case uint64: 114 i, err := s.Uint() 115 if err != nil { 116 return addStack("Uint", exp, err) 117 } 118 if i != exp { 119 return addStack("Uint", exp, fmt.Errorf("result mismatch: got %d", i)) 120 } 121 case *big.Int: 122 big := new(big.Int) 123 if err := s.Decode(&big); err != nil { 124 return addStack("Big", exp, err) 125 } 126 if big.Cmp(exp) != 0 { 127 return addStack("Big", exp, fmt.Errorf("result mismatch: got %d", big)) 128 } 129 case []byte: 130 b, err := s.Bytes() 131 if err != nil { 132 return addStack("Bytes", exp, err) 133 } 134 if !bytes.Equal(b, exp) { 135 return addStack("Bytes", exp, fmt.Errorf("result mismatch: got %x", b)) 136 } 137 case []interface{}: 138 if _, err := s.List(); err != nil { 139 return addStack("List", exp, err) 140 } 141 for i, v := range exp { 142 if err := checkDecodeFromJSON(s, v); err != nil { 143 return addStack(fmt.Sprintf("[%d]", i), exp, err) 144 } 145 } 146 if err := s.ListEnd(); err != nil { 147 return addStack("ListEnd", exp, err) 148 } 149 default: 150 panic(fmt.Errorf("unhandled type: %T", exp)) 151 } 152 return nil 153 } 154 155 func addStack(op string, val interface{}, err error) error { 156 lines := strings.Split(err.Error(), "\n") 157 lines = append(lines, fmt.Sprintf("\t%s: %v", op, val)) 158 return errors.New(strings.Join(lines, "\n")) 159 }