github.com/aiyaya188/klaytn@v0.0.0-20220629133911-2c66fd5546f4/cmd/rlpdump/main.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // rlpdump is a pretty-printer for RLP data.
    18  package main
    19  
    20  import (
    21  	"bufio"
    22  	"bytes"
    23  	"container/list"
    24  	"encoding/hex"
    25  	"flag"
    26  	"fmt"
    27  	"io"
    28  	"os"
    29  	"strings"
    30  
    31  	"github.com/aiyaya188/klaytn/common"
    32  	"github.com/aiyaya188/klaytn/rlp"
    33  )
    34  
    35  var (
    36  	hexMode     = flag.String("hex", "", "dump given hex data")
    37  	reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
    38  	noASCII     = flag.Bool("noascii", false, "don't print ASCII strings readably")
    39  	single      = flag.Bool("single", false, "print only the first element, discard the rest")
    40  )
    41  
    42  func init() {
    43  	flag.Usage = func() {
    44  		fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>][-reverse] [filename]")
    45  		flag.PrintDefaults()
    46  		fmt.Fprintln(os.Stderr, `
    47  Dumps RLP data from the given file in readable form.
    48  If the filename is omitted, data is read from stdin.`)
    49  	}
    50  }
    51  
    52  func main() {
    53  	flag.Parse()
    54  
    55  	var r io.Reader
    56  	switch {
    57  	case *hexMode != "":
    58  		data, err := hex.DecodeString(strings.TrimPrefix(*hexMode, "0x"))
    59  		if err != nil {
    60  			die(err)
    61  		}
    62  		r = bytes.NewReader(data)
    63  
    64  	case flag.NArg() == 0:
    65  		r = os.Stdin
    66  
    67  	case flag.NArg() == 1:
    68  		fd, err := os.Open(flag.Arg(0))
    69  		if err != nil {
    70  			die(err)
    71  		}
    72  		defer fd.Close()
    73  		r = fd
    74  
    75  	default:
    76  		fmt.Fprintln(os.Stderr, "Error: too many arguments")
    77  		flag.Usage()
    78  		os.Exit(2)
    79  	}
    80  	out := os.Stdout
    81  	if *reverseMode {
    82  		data, err := textToRlp(r)
    83  		if err != nil {
    84  			die(err)
    85  		}
    86  		fmt.Printf("0x%x\n", data)
    87  		return
    88  	} else {
    89  		err := rlpToText(r, out)
    90  		if err != nil {
    91  			die(err)
    92  		}
    93  	}
    94  }
    95  
    96  func rlpToText(r io.Reader, out io.Writer) error {
    97  	s := rlp.NewStream(r, 0)
    98  	for {
    99  		if err := dump(s, 0, out); err != nil {
   100  			if err != io.EOF {
   101  				return err
   102  			}
   103  			break
   104  		}
   105  		fmt.Fprintln(out)
   106  		if *single {
   107  			break
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  func dump(s *rlp.Stream, depth int, out io.Writer) error {
   114  	kind, size, err := s.Kind()
   115  	if err != nil {
   116  		return err
   117  	}
   118  	switch kind {
   119  	case rlp.Byte, rlp.String:
   120  		str, err := s.Bytes()
   121  		if err != nil {
   122  			return err
   123  		}
   124  		if len(str) == 0 || !*noASCII && isASCII(str) {
   125  			fmt.Fprintf(out, "%s%q", ws(depth), str)
   126  		} else {
   127  			fmt.Fprintf(out, "%s%x", ws(depth), str)
   128  		}
   129  	case rlp.List:
   130  		s.List()
   131  		defer s.ListEnd()
   132  		if size == 0 {
   133  			fmt.Fprintf(out, ws(depth)+"[]")
   134  		} else {
   135  			fmt.Fprintln(out, ws(depth)+"[")
   136  			for i := 0; ; i++ {
   137  				if i > 0 {
   138  					fmt.Fprint(out, ",\n")
   139  				}
   140  				if err := dump(s, depth+1, out); err == rlp.EOL {
   141  					break
   142  				} else if err != nil {
   143  					return err
   144  				}
   145  			}
   146  			fmt.Fprint(out, ws(depth)+"]")
   147  		}
   148  	}
   149  	return nil
   150  }
   151  
   152  func isASCII(b []byte) bool {
   153  	for _, c := range b {
   154  		if c < 32 || c > 126 {
   155  			return false
   156  		}
   157  	}
   158  	return true
   159  }
   160  
   161  func ws(n int) string {
   162  	return strings.Repeat("  ", n)
   163  }
   164  
   165  func die(args ...interface{}) {
   166  	fmt.Fprintln(os.Stderr, args...)
   167  	os.Exit(1)
   168  }
   169  
   170  // textToRlp converts text into RLP (best effort).
   171  func textToRlp(r io.Reader) ([]byte, error) {
   172  	// We're expecting the input to be well-formed, meaning that
   173  	// - each element is on a separate line
   174  	// - each line is either an (element OR a list start/end) + comma
   175  	// - an element is either hex-encoded bytes OR a quoted string
   176  	var (
   177  		scanner = bufio.NewScanner(r)
   178  		obj     []interface{}
   179  		stack   = list.New()
   180  	)
   181  	for scanner.Scan() {
   182  		t := strings.TrimSpace(scanner.Text())
   183  		if len(t) == 0 {
   184  			continue
   185  		}
   186  		switch t {
   187  		case "[": // list start
   188  			stack.PushFront(obj)
   189  			obj = make([]interface{}, 0)
   190  		case "]", "],": // list end
   191  			parent := stack.Remove(stack.Front()).([]interface{})
   192  			obj = append(parent, obj)
   193  		case "[],": // empty list
   194  			obj = append(obj, make([]interface{}, 0))
   195  		default: // element
   196  			data := []byte(t)[:len(t)-1] // cut off comma
   197  			if data[0] == '"' {          // ascii string
   198  				data = []byte(t)[1 : len(data)-1]
   199  			} else { // hex data
   200  				data = common.FromHex(string(data))
   201  			}
   202  			obj = append(obj, data)
   203  		}
   204  	}
   205  	if err := scanner.Err(); err != nil {
   206  		return nil, err
   207  	}
   208  	data, err := rlp.EncodeToBytes(obj[0])
   209  	return data, err
   210  }