| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | package tests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | // RLPTest is the JSON structure of a single RLP test. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | type RLPTest struct { | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | 	// If the value of In is "INVALID" or "VALID", the test | 
					
						
							|  |  |  | 	// checks whether Out can be decoded into a value of | 
					
						
							|  |  |  | 	// type interface{}. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// For other JSON values, In is treated as a driver for | 
					
						
							|  |  |  | 	// calls to rlp.Stream. The test also verifies that encoding | 
					
						
							|  |  |  | 	// In produces the bytes in Out. | 
					
						
							|  |  |  | 	In interface{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Out is a hex-encoded RLP value. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | 	Out string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | // RunRLPTest runs the tests in the given file, skipping tests by name. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | func RunRLPTest(file string, skip []string) error { | 
					
						
							|  |  |  | 	f, err := os.Open(file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer f.Close() | 
					
						
							|  |  |  | 	return RunRLPTestWithReader(f, skip) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | // RunRLPTest runs the tests encoded in r, skipping tests by name. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | func RunRLPTestWithReader(r io.Reader, skip []string) error { | 
					
						
							|  |  |  | 	var tests map[string]*RLPTest | 
					
						
							|  |  |  | 	if err := readJson(r, &tests); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, s := range skip { | 
					
						
							|  |  |  | 		delete(tests, s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for name, test := range tests { | 
					
						
							|  |  |  | 		if err := test.Run(); err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("test %q failed: %v", name, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Run executes the test. | 
					
						
							|  |  |  | func (t *RLPTest) Run() error { | 
					
						
							|  |  |  | 	outb, err := hex.DecodeString(t.Out) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("invalid hex in Out") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Handle simple decoding tests with no actual In value. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | 	if t.In == "VALID" || t.In == "INVALID" { | 
					
						
							|  |  |  | 		return checkDecodeInterface(outb, t.In == "VALID") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check whether encoding the value produces the same bytes. | 
					
						
							|  |  |  | 	in := translateJSON(t.In) | 
					
						
							|  |  |  | 	b, err := rlp.EncodeToBytes(in) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("encode failed: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !bytes.Equal(b, outb) { | 
					
						
							|  |  |  | 		return fmt.Errorf("encode produced %x, want %x", b, outb) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | 	// Test stream decoding. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | 	s := rlp.NewStream(bytes.NewReader(outb), 0) | 
					
						
							|  |  |  | 	return checkDecodeFromJSON(s, in) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func checkDecodeInterface(b []byte, isValid bool) error { | 
					
						
							|  |  |  | 	err := rlp.DecodeBytes(b, new(interface{})) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case isValid && err != nil: | 
					
						
							|  |  |  | 		return fmt.Errorf("decoding failed: %v", err) | 
					
						
							|  |  |  | 	case !isValid && err == nil: | 
					
						
							|  |  |  | 		return fmt.Errorf("decoding of invalid value succeeded") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // translateJSON makes test json values encodable with RLP. | 
					
						
							|  |  |  | func translateJSON(v interface{}) interface{} { | 
					
						
							|  |  |  | 	switch v := v.(type) { | 
					
						
							|  |  |  | 	case float64: | 
					
						
							|  |  |  | 		return uint64(v) | 
					
						
							|  |  |  | 	case string: | 
					
						
							|  |  |  | 		if len(v) > 0 && v[0] == '#' { // # starts a faux big int. | 
					
						
							|  |  |  | 			big, ok := new(big.Int).SetString(v[1:], 10) | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				panic(fmt.Errorf("bad test: bad big int: %q", v)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return big | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return []byte(v) | 
					
						
							|  |  |  | 	case []interface{}: | 
					
						
							|  |  |  | 		new := make([]interface{}, len(v)) | 
					
						
							|  |  |  | 		for i := range v { | 
					
						
							|  |  |  | 			new[i] = translateJSON(v[i]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return new | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic(fmt.Errorf("can't handle %T", v)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-17 15:42:23 +02:00
										 |  |  | // checkDecodeFromJSON decodes from s guided by exp. exp drives the | 
					
						
							|  |  |  | // Stream by invoking decoding operations (Uint, Big, List, ...) based | 
					
						
							|  |  |  | // on the type of each value. The value decoded from the RLP stream | 
					
						
							|  |  |  | // must match the JSON value. | 
					
						
							| 
									
										
										
										
											2015-07-17 15:13:24 +02:00
										 |  |  | func checkDecodeFromJSON(s *rlp.Stream, exp interface{}) error { | 
					
						
							|  |  |  | 	switch exp := exp.(type) { | 
					
						
							|  |  |  | 	case uint64: | 
					
						
							|  |  |  | 		i, err := s.Uint() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return addStack("Uint", exp, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if i != exp { | 
					
						
							|  |  |  | 			return addStack("Uint", exp, fmt.Errorf("result mismatch: got %d", i)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case *big.Int: | 
					
						
							|  |  |  | 		big := new(big.Int) | 
					
						
							|  |  |  | 		if err := s.Decode(&big); err != nil { | 
					
						
							|  |  |  | 			return addStack("Big", exp, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if big.Cmp(exp) != 0 { | 
					
						
							|  |  |  | 			return addStack("Big", exp, fmt.Errorf("result mismatch: got %d", big)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case []byte: | 
					
						
							|  |  |  | 		b, err := s.Bytes() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return addStack("Bytes", exp, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !bytes.Equal(b, exp) { | 
					
						
							|  |  |  | 			return addStack("Bytes", exp, fmt.Errorf("result mismatch: got %x", b)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case []interface{}: | 
					
						
							|  |  |  | 		if _, err := s.List(); err != nil { | 
					
						
							|  |  |  | 			return addStack("List", exp, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for i, v := range exp { | 
					
						
							|  |  |  | 			if err := checkDecodeFromJSON(s, v); err != nil { | 
					
						
							|  |  |  | 				return addStack(fmt.Sprintf("[%d]", i), exp, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := s.ListEnd(); err != nil { | 
					
						
							|  |  |  | 			return addStack("ListEnd", exp, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic(fmt.Errorf("unhandled type: %T", exp)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func addStack(op string, val interface{}, err error) error { | 
					
						
							|  |  |  | 	lines := strings.Split(err.Error(), "\n") | 
					
						
							|  |  |  | 	lines = append(lines, fmt.Sprintf("\t%s: %v", op, val)) | 
					
						
							|  |  |  | 	return errors.New(strings.Join(lines, "\n")) | 
					
						
							|  |  |  | } |