github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/cmd/aminoscan/aminoscan.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/hex"
     6  	"errors"
     7  	"flag"
     8  	"fmt"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  
    13  	amino "github.com/gnolang/gno/tm2/pkg/amino"
    14  )
    15  
    16  func main() {
    17  	// Print help.
    18  	if len(os.Args) == 1 {
    19  		fmt.Println(`Usage: aminoscan <STRUCT HEXBYTES> or --help`)
    20  		return
    21  	}
    22  
    23  	// Parse flags...
    24  	var colorize bool
    25  	flgs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
    26  	flgs.BoolVar(&colorize, "color", false, "Just print the colored bytes and exit.")
    27  	err := flgs.Parse(os.Args[1:])
    28  	if errors.Is(err, flag.ErrHelp) {
    29  		fmt.Println(`Usage: aminoscan <STRUCT HEXBYTES> or --help
    30  		
    31  		You can also use aminoscan to print "colored" bytes.  This view will
    32  		try to display bytes in ascii in a different color if it happens to be
    33  		a printable character.
    34  
    35  		> aminoscan --color <HEXBYTES>`)
    36  		return
    37  	} else if err != nil {
    38  		fmt.Println(err)
    39  		return
    40  	}
    41  
    42  	// If we just want to show colored bytes...
    43  	if colorize {
    44  		if flgs.Arg(0) == "" {
    45  			fmt.Println(`Usage: aminoscan --color <HEXBYTES>`)
    46  			return
    47  		}
    48  		bz := hexDecode(flgs.Arg(0))
    49  		fmt.Println(ColoredBytes(bz, Green, Blue))
    50  		return
    51  	}
    52  
    53  	// Parse struct Amino bytes.
    54  	bz := hexDecode(os.Args[1]) // Read input hex bytes.
    55  	fmt.Println(Yellow("## Root Struct (assumed)"))
    56  	s, n, err := scanStruct(bz, "", true) // Assume that it's  struct.
    57  	s += Red(fmt.Sprintf("%X", bz[n:]))   // Bytes remaining are red.
    58  	fmt.Println(Yellow("## Root Struct END"))
    59  	fmt.Println(s, n, err) // Print color-encoded bytes s.
    60  }
    61  
    62  func scanAny(typ amino.Typ3, bz []byte, indent string) (s string, n int, err error) {
    63  	switch typ {
    64  	case amino.Typ3Varint:
    65  		s, n, err = scanVarint(bz, indent)
    66  	case amino.Typ38Byte:
    67  		s, n, err = scan8Byte(bz, indent)
    68  	case amino.Typ3ByteLength:
    69  		s, n, err = scanByteLength(bz, indent)
    70  	case amino.Typ34Byte:
    71  		s, n, err = scan4Byte(bz, indent)
    72  	default:
    73  		panic("should not happen")
    74  	}
    75  	return
    76  }
    77  
    78  func scanVarint(bz []byte, indent string) (s string, n int, err error) {
    79  	if len(bz) == 0 {
    80  		err = fmt.Errorf("EOF while reading (U)Varint")
    81  	}
    82  	// First try Varint.
    83  	okI64 := true
    84  	i64, n := binary.Varint(bz)
    85  	if n <= 0 {
    86  		n = 0
    87  		okI64 = false
    88  	}
    89  	// Then try Uvarint.
    90  	okU64 := true
    91  	u64, _n := binary.Uvarint(bz)
    92  	if n != _n {
    93  		n = 0
    94  		okU64 = false
    95  	}
    96  	// If neither work, return error.
    97  	if !okI64 && !okU64 {
    98  		err = fmt.Errorf("invalid (u)varint")
    99  		return
   100  	}
   101  	// s is the same either way.
   102  	s = Cyan(fmt.Sprintf("%X", bz[:n]))
   103  	fmt.Printf("%s%s (", indent, s)
   104  	if okI64 {
   105  		fmt.Printf("i64:%v ", i64)
   106  	}
   107  	if okU64 {
   108  		fmt.Printf("u64:%v", u64)
   109  	}
   110  	fmt.Print(")\n")
   111  	return s, n, err
   112  }
   113  
   114  func scan8Byte(bz []byte, indent string) (s string, n int, err error) {
   115  	if len(bz) < 8 {
   116  		err = errors.New("while reading 8byte field, EOF was encountered")
   117  		return
   118  	}
   119  	n = 8
   120  	s = Blue(fmt.Sprintf("%X", bz[:8]))
   121  	fmt.Printf("%s%s\n", indent, s)
   122  	return
   123  }
   124  
   125  func scanByteLength(bz []byte, indent string) (s string, n int, err error) {
   126  	// Read the length.
   127  	l64, _n := binary.Uvarint(bz)
   128  	if n < 0 {
   129  		n = 0
   130  		err = errors.New("error decoding uvarint")
   131  		return
   132  	}
   133  	length := int(l64)
   134  	if length >= len(bz) {
   135  		err = errors.New("while reading 8byte field, EOF was encountered")
   136  		return
   137  	}
   138  	lengthStrLong := fmt.Sprintf("%X (%v bytes) ", bz[:_n], length)
   139  	lengthStrLongLen := len(lengthStrLong) // for indenting later
   140  	lengthStrShort := fmt.Sprintf("%X", bz[:_n])
   141  	s = Cyan(lengthStrShort)
   142  	slide(&bz, &n, _n)
   143  	// Read the remaining bytes.
   144  	contents := bz[:length]
   145  	contentsStr := fmt.Sprintf("%X", contents)
   146  	s += Green(contentsStr)
   147  	slide(&bz, &n, length)
   148  	fmt.Printf("%s%s%s\n", indent, Cyan(lengthStrLong), Green(contentsStr))
   149  	// If ascii string, also show the string in quotes.
   150  	if amino.IsASCIIText(string(contents)) {
   151  		fmt.Printf("%s%s\n", indent+strings.Repeat(" ", lengthStrLongLen), Green("("+strconv.Quote(string(contents))+" in ASCII)"))
   152  	}
   153  	return
   154  }
   155  
   156  func scanStruct(bz []byte, indent string, isRoot bool) (s string, n int, err error) {
   157  	var (
   158  		_s  string
   159  		_n  int
   160  		typ amino.Typ3
   161  	)
   162  	for {
   163  		if isRoot && len(bz) == 0 {
   164  			return
   165  		}
   166  		_s, typ, _n, err = scanFieldKey(bz, indent+"  ")
   167  		if slide(&bz, &n, _n) && concat(&s, _s) && err != nil {
   168  			return
   169  		}
   170  		_s, _n, err = scanAny(typ, bz, indent+"  ")
   171  		if slide(&bz, &n, _n) && concat(&s, _s) && err != nil {
   172  			return
   173  		}
   174  	}
   175  }
   176  
   177  func scanFieldKey(bz []byte, indent string) (s string, typ amino.Typ3, n int, err error) {
   178  	var u64 uint64
   179  	u64, n = binary.Uvarint(bz)
   180  	if n < 0 {
   181  		n = 0
   182  		err = errors.New("error decoding uvarint")
   183  		return
   184  	}
   185  	typ = amino.Typ3(u64 & 0x07)
   186  	number := uint32(u64 >> 3)
   187  	s = fmt.Sprintf("%X", bz[:n])
   188  	fmt.Printf("%s%s @%v %v\n", indent, s, number, typ)
   189  	return
   190  }
   191  
   192  func scan4Byte(bz []byte, indent string) (s string, n int, err error) {
   193  	if len(bz) < 4 {
   194  		err = errors.New("while reading 8byte field, EOF was encountered")
   195  		return
   196  	}
   197  	n = 4
   198  	s = Blue(fmt.Sprintf("%X", bz[:4]))
   199  	fmt.Printf("%s%s\n", indent, s)
   200  	return
   201  }
   202  
   203  /*
   204  func scanList(bz []byte, indent string) (s string, n int, err error) {
   205  	// Read element Typ4.
   206  	if len(bz) < 1 {
   207  		err = errors.New("EOF while reading list element typ4.")
   208  		return
   209  	}
   210  	var typ = amino.Typ4(bz[0])
   211  	if typ&0xF0 > 0 {
   212  		err = errors.New("Invalid list element typ4 byte")
   213  	}
   214  	s = fmt.Sprintf("%X", bz[:1])
   215  	if slide(&bz, &n, 1) && err != nil {
   216  		return
   217  	}
   218  	// Read number of elements.
   219  	var num, _n = uint64(0), int(0)
   220  	num, _n = binary.Uvarint(bz)
   221  	if _n < 0 {
   222  		_n = 0
   223  		err = errors.New("error decoding list length (uvarint)")
   224  	}
   225  	s += Cyan(fmt.Sprintf("%X", bz[:_n]))
   226  	if slide(&bz, &n, _n) && err != nil {
   227  		return
   228  	}
   229  	fmt.Printf("%s%s of %v with %v items\n", indent, s, typ, num)
   230  	// Read elements.
   231  	var _s string
   232  	for i := 0; i < int(num); i++ {
   233  		// Maybe read nil byte.
   234  		if typ&0x08 != 0 {
   235  			if len(bz) == 0 {
   236  				err = errors.New("EOF while reading list nil byte")
   237  				return
   238  			}
   239  			var nb = bz[0]
   240  			slide(&bz, &n, 1)
   241  			switch nb {
   242  			case 0x00:
   243  				s += "00"
   244  				fmt.Printf("%s00 (not nil)\n", indent)
   245  			case 0x01:
   246  				s += "01" // Is nil (NOTE: reverse logic)
   247  				fmt.Printf("%s01 (is nil)\n", indent)
   248  				continue
   249  			default:
   250  				err = fmt.Errorf("Unexpected nil pointer byte %X", nb)
   251  				return
   252  			}
   253  		}
   254  		// Read element.
   255  		_s, _n, err = scanAny(typ.Typ3(), bz, indent+"  ")
   256  		if slide(&bz, &n, _n) && concat(&s, _s) && err != nil {
   257  			return
   258  		}
   259  	}
   260  	return
   261  }
   262  */
   263  
   264  /*
   265  func scanInterface(bz []byte, indent string) (s string, n int, err error) {
   266  	db, hasDb, pb, typ, _, isNil, _n, err := amino.DecodeDisambPrefixBytes(bz)
   267  	if slide(&bz, &n, _n) && err != nil {
   268  		return
   269  	}
   270  	pb3 := pb
   271  	if isNil {
   272  		s = Magenta("0000")
   273  	} else if hasDb {
   274  		s = Magenta(fmt.Sprintf("%X%X", db.Bytes(), pb3.Bytes()))
   275  	} else {
   276  		s = Magenta(fmt.Sprintf("%X", pb3.Bytes()))
   277  	}
   278  	if isNil {
   279  		fmt.Printf("%s%s (nil interface)\n", indent, s)
   280  	} else if hasDb {
   281  		fmt.Printf("%s%s (disamb: %X, prefix: %X, typ: %v)\n",
   282  			indent, s, db.Bytes(), pb.Bytes(), typ)
   283  	} else {
   284  		fmt.Printf("%s%s (prefix: %X, typ: %v)\n",
   285  			indent, s, pb.Bytes(), typ)
   286  	}
   287  	_s, _n, err := scanAny(typ, bz, indent)
   288  	if slide(&bz, &n, _n) && concat(&s, _s) && err != nil {
   289  		return
   290  	}
   291  	return
   292  }
   293  */
   294  
   295  //----------------------------------------
   296  // Misc.
   297  
   298  func slide(bzPtr *[]byte, n *int, _n int) bool {
   299  	if len(*bzPtr) < _n {
   300  		panic("eof")
   301  	}
   302  	*bzPtr = (*bzPtr)[_n:]
   303  	*n += _n
   304  	return true
   305  }
   306  
   307  func concat(sPtr *string, _s string) bool {
   308  	*sPtr += _s
   309  	return true
   310  }
   311  
   312  func hexDecode(s string) []byte {
   313  	bz, err := hex.DecodeString(s)
   314  	if err != nil {
   315  		panic(err)
   316  	}
   317  	return bz
   318  }