cmd/rlpdump: add support for text to rlp (#23745)
This PR adds support for the rlpdump tool to go from text format to RLP.
This commit is contained in:
parent
eaa24a8a15
commit
b8dc1e2705
@ -18,7 +18,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"container/list"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -26,18 +28,20 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hexMode = flag.String("hex", "", "dump given hex data")
|
hexMode = flag.String("hex", "", "dump given hex data")
|
||||||
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
|
reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
|
||||||
single = flag.Bool("single", false, "print only the first element, discard the rest")
|
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
|
||||||
|
single = flag.Bool("single", false, "print only the first element, discard the rest")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>] [filename]")
|
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex <data>][-reverse] [filename]")
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
fmt.Fprintln(os.Stderr, `
|
fmt.Fprintln(os.Stderr, `
|
||||||
Dumps RLP data from the given file in readable form.
|
Dumps RLP data from the given file in readable form.
|
||||||
@ -73,23 +77,40 @@ func main() {
|
|||||||
flag.Usage()
|
flag.Usage()
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
out := os.Stdout
|
||||||
s := rlp.NewStream(r, 0)
|
if *reverseMode {
|
||||||
for {
|
data, err := textToRlp(r)
|
||||||
if err := dump(s, 0); err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
die(err)
|
||||||
die(err)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Printf("0x%x\n", data)
|
||||||
if *single {
|
return
|
||||||
break
|
} else {
|
||||||
|
err := rlpToText(r, out)
|
||||||
|
if err != nil {
|
||||||
|
die(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dump(s *rlp.Stream, depth int) error {
|
func rlpToText(r io.Reader, out io.Writer) error {
|
||||||
|
s := rlp.NewStream(r, 0)
|
||||||
|
for {
|
||||||
|
if err := dump(s, 0, out); err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Fprintln(out)
|
||||||
|
if *single {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dump(s *rlp.Stream, depth int, out io.Writer) error {
|
||||||
kind, size, err := s.Kind()
|
kind, size, err := s.Kind()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -101,28 +122,28 @@ func dump(s *rlp.Stream, depth int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(str) == 0 || !*noASCII && isASCII(str) {
|
if len(str) == 0 || !*noASCII && isASCII(str) {
|
||||||
fmt.Printf("%s%q", ws(depth), str)
|
fmt.Fprintf(out, "%s%q", ws(depth), str)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s%x", ws(depth), str)
|
fmt.Fprintf(out, "%s%x", ws(depth), str)
|
||||||
}
|
}
|
||||||
case rlp.List:
|
case rlp.List:
|
||||||
s.List()
|
s.List()
|
||||||
defer s.ListEnd()
|
defer s.ListEnd()
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
fmt.Print(ws(depth) + "[]")
|
fmt.Fprintf(out, ws(depth)+"[]")
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(ws(depth) + "[")
|
fmt.Fprintln(out, ws(depth)+"[")
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
fmt.Print(",\n")
|
fmt.Fprint(out, ",\n")
|
||||||
}
|
}
|
||||||
if err := dump(s, depth+1); err == rlp.EOL {
|
if err := dump(s, depth+1, out); err == rlp.EOL {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Print(ws(depth) + "]")
|
fmt.Fprint(out, ws(depth)+"]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -145,3 +166,45 @@ func die(args ...interface{}) {
|
|||||||
fmt.Fprintln(os.Stderr, args...)
|
fmt.Fprintln(os.Stderr, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// textToRlp converts text into RLP (best effort).
|
||||||
|
func textToRlp(r io.Reader) ([]byte, error) {
|
||||||
|
// We're expecting the input to be well-formed, meaning that
|
||||||
|
// - each element is on a separate line
|
||||||
|
// - each line is either an (element OR a list start/end) + comma
|
||||||
|
// - an element is either hex-encoded bytes OR a quoted string
|
||||||
|
var (
|
||||||
|
scanner = bufio.NewScanner(r)
|
||||||
|
obj []interface{}
|
||||||
|
stack = list.New()
|
||||||
|
)
|
||||||
|
for scanner.Scan() {
|
||||||
|
t := strings.TrimSpace(scanner.Text())
|
||||||
|
if len(t) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch t {
|
||||||
|
case "[": // list start
|
||||||
|
stack.PushFront(obj)
|
||||||
|
obj = make([]interface{}, 0)
|
||||||
|
case "]", "],": // list end
|
||||||
|
parent := stack.Remove(stack.Front()).([]interface{})
|
||||||
|
obj = append(parent, obj)
|
||||||
|
case "[],": // empty list
|
||||||
|
obj = append(obj, make([]interface{}, 0))
|
||||||
|
default: // element
|
||||||
|
data := []byte(t)[:len(t)-1] // cut off comma
|
||||||
|
if data[0] == '"' { // ascii string
|
||||||
|
data = []byte(t)[1 : len(data)-1]
|
||||||
|
} else { // hex data
|
||||||
|
data = common.FromHex(string(data))
|
||||||
|
}
|
||||||
|
obj = append(obj, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := rlp.EncodeToBytes(obj[0])
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
65
cmd/rlpdump/rlpdump_test.go
Normal file
65
cmd/rlpdump/rlpdump_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoundtrip(t *testing.T) {
|
||||||
|
for i, want := range []string{
|
||||||
|
"0xf880806482520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0a1010000000000000000000000000000000000000000000000000000000000000001801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28",
|
||||||
|
"0xd5c0d3cb84746573742a2a808213378667617a6f6e6b",
|
||||||
|
"0xc780c0c1c0825208",
|
||||||
|
} {
|
||||||
|
var out strings.Builder
|
||||||
|
err := rlpToText(bytes.NewReader(common.FromHex(want)), &out)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
text := out.String()
|
||||||
|
rlpBytes, err := textToRlp(strings.NewReader(text))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
have := fmt.Sprintf("0x%x", rlpBytes)
|
||||||
|
if have != want {
|
||||||
|
t.Errorf("test %d: have\n%v\nwant:\n%v\n", i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextToRlp(t *testing.T) {
|
||||||
|
type tc struct {
|
||||||
|
text string
|
||||||
|
want string
|
||||||
|
}
|
||||||
|
cases := []tc{
|
||||||
|
{
|
||||||
|
text: `[
|
||||||
|
"",
|
||||||
|
[],
|
||||||
|
[
|
||||||
|
[],
|
||||||
|
],
|
||||||
|
5208,
|
||||||
|
]`,
|
||||||
|
want: "0xc780c0c1c0825208",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range cases {
|
||||||
|
have, err := textToRlp(strings.NewReader(tc.text))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d: error %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if hexutil.Encode(have) != tc.want {
|
||||||
|
t.Errorf("test %d:\nhave %v\nwant %v", i, hexutil.Encode(have), tc.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user