github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/explore/validate.go (about) 1 // Copyright 2021 The TrueBlocks Authors. All rights reserved. 2 // Use of this source code is governed by a license that can 3 // be found in the LICENSE file. 4 5 package explorePkg 6 7 import ( 8 "fmt" 9 "strings" 10 11 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 12 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 14 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate" 15 "github.com/ethereum/go-ethereum" 16 ) 17 18 func (opts *ExploreOptions) validateExplore() error { 19 chain := opts.Globals.Chain 20 21 opts.testLog() 22 23 if opts.BadFlag != nil { 24 return opts.BadFlag 25 } 26 27 if !config.IsChainConfigured(chain) { 28 return validate.Usage("chain {0} is not properly configured.", chain) 29 } 30 31 if opts.Globals.IsApiMode() { 32 return validate.Usage("The {0} option is not available{1}.", "explore", " in api mode") 33 } 34 35 if opts.Google && opts.Local { 36 return validate.Usage("The {0} option is not available{1}.", "--local", " with the --google option") 37 } 38 39 if opts.Dalle && opts.Local { 40 return validate.Usage("The {0} option is not available{1}.", "--local", " with the --dalle option") 41 } 42 43 if len(opts.Terms) == 0 && (opts.Dalle || opts.Google) { 44 return validate.Usage("The {0} options require {1}.", "--dalle and --google", "an address term") 45 } 46 47 for _, arg := range opts.Terms { 48 arg = strings.ToLower(arg) 49 50 if base.IsValidAddress(arg) { 51 if strings.Contains(arg, ".eth") { 52 opts.Destinations = append(opts.Destinations, types.NewDestination(arg, types.DestinationEnsName)) 53 } else { 54 opts.Destinations = append(opts.Destinations, types.NewDestination(arg, types.DestinationAddress)) 55 } 56 // We got a valid address, we're done checking 57 continue 58 } 59 60 // The argument is not an address, so we can't use --google 61 if opts.Google || opts.Dalle { 62 continue 63 } 64 65 valid, _ := validate.IsValidTransId(chain, []string{arg}, validate.ValidTransId) 66 if valid { 67 txHash, err := opts.idToTxHash(arg, validate.IsBlockHash) 68 if err == nil { 69 opts.Destinations = append(opts.Destinations, types.NewDestination(txHash, types.DestinationTx)) 70 continue 71 } 72 // an error here is okay since we can't distinquish between tx hashes and block hashes... 73 } 74 75 valid, _ = validate.IsValidBlockId(chain, []string{arg}, validate.ValidBlockIdWithRangeAndDate) 76 if valid { 77 blockHash, err := opts.idToBlockHash(chain, arg, validate.IsBlockHash) 78 if err == nil { 79 opts.Destinations = append(opts.Destinations, types.NewDestination(blockHash.Hex(), types.DestinationBlock)) 80 continue 81 } 82 // An error here is not okay because we have a valid hash but it's not a valid on-chain 83 // thingy, so we must have been told why by the node 84 return fmt.Errorf("block at %s returned an error: %w", arg, ethereum.NotFound) 85 } 86 87 if validate.IsValidFourByte(arg) { 88 opts.Destinations = append(opts.Destinations, types.NewDestination(arg, types.DestinationFourByte)) 89 continue 90 } 91 92 return validate.Usage("The {0} option ({1}) {2}.", "term", arg, "is not valid") 93 } 94 95 if len(opts.Destinations) == 0 { 96 if opts.Google || opts.Dalle { 97 return validate.Usage("The {0} options require {1}.", "--dalle and --google", "an address term") 98 } 99 opts.Destinations = append(opts.Destinations, types.NewDestination("", types.DestinationNone)) 100 } 101 102 return opts.Globals.Validate() 103 } 104 105 func (opts *ExploreOptions) idToBlockHash(chain, arg string, isBlockHash func(arg string) bool) (base.Hash, error) { 106 _ = chain // linter 107 if isBlockHash(arg) { 108 return opts.Conn.GetBlockHashByHash(arg) 109 } 110 return opts.Conn.GetBlockHashByNumber(base.MustParseBlknum(arg)) 111 } 112 113 // idToTxHash takes a valid identifier (txHash/blockHash, blockHash.txId, blockNumber.txId) 114 // and returns the transaction hash represented by that identifier. (If it's a valid transaction. 115 // It may not be because transaction hashes and block hashes are both 32-byte hex) 116 func (opts *ExploreOptions) idToTxHash(arg string, isBlockHash func(arg string) bool) (string, error) { 117 // simple case first 118 if !strings.Contains(arg, ".") { 119 // We know it's a hash, but we want to know if it's a legitimate tx on chain 120 return opts.Conn.GetTransactionHashByHash(arg) 121 } 122 123 parts := strings.Split(arg, ".") 124 if len(parts) != 2 { 125 panic("Programmer error - valid transaction identifiers with a `.` must have two and only two parts") 126 } 127 128 if isBlockHash(parts[0]) { 129 return opts.Conn.GetTransactionHashByHashAndID(parts[0], base.MustParseTxnum(parts[1])) 130 } 131 132 blockNum := base.MustParseBlknum(parts[0]) 133 txId := base.MustParseTxnum(parts[1]) 134 hash, err := opts.Conn.GetTransactionHashByNumberAndID(blockNum, base.Txnum(txId)) 135 return hash.Hex(), err 136 }