github.com/theQRL/go-zond@v0.1.1/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/theQRL/go-zond/common" 32 "github.com/theQRL/go-zond/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("%#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 }