github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/validate/validateargs.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 validate 6 7 import ( 8 "strings" 9 10 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 11 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers" 12 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 13 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/tslib" 14 ) 15 16 // ValidArgumentType is a bitmask used to make it easier to validate multiple 17 // block identifiers (which usually are arguments to `chifra` commands). This 18 // way we can specify that we want e.g. both block number and a special block 19 // as valid arguments 20 type ValidArgumentType uint16 21 22 const ( 23 ValidArgumentBlockHash ValidArgumentType = 1 << iota 24 ValidArgumentBlockNumber 25 ValidArgumentTimestamp 26 ValidArgumentDate 27 ValidArgumentRange 28 ValidArgumentSpecialBlock 29 ValidArgumentTransHash 30 ValidArgumentTransBlockNumberAndId 31 ValidArgumentTransBlockHashAndId 32 ) 33 34 const ValidTransId = ValidArgumentTransHash | ValidArgumentTransBlockNumberAndId | ValidArgumentTransBlockHashAndId 35 const ValidBlockId = ValidArgumentBlockHash | ValidArgumentBlockNumber | ValidArgumentTimestamp | ValidArgumentSpecialBlock 36 const ValidBlockIdWithRange = ValidBlockId | ValidArgumentRange 37 const ValidBlockIdWithRangeAndDate = ValidBlockIdWithRange | ValidArgumentDate 38 39 // ValidateIdentifiersWithBounds Is a helper function to return bounds in addition to validating identifiers 40 func ValidateIdentifiersWithBounds(chain string, ids []string, validTypes ValidArgumentType, maxRanges int, results *[]identifiers.Identifier) (base.BlockRange, error) { 41 if err := ValidateIdentifiers(chain, ids, validTypes, maxRanges, results); err != nil { 42 return base.BlockRange{First: 0, Last: base.NOPOSN}, err 43 } 44 return identifiers.GetBounds(chain, results) 45 } 46 47 // ValidateIdentifiers validates multiple identifiers against multiple valid types (specified as bitmasks). 48 // If any of the identifiers are invalid, it returns error 49 // If all identifiers are valid, it returns nil 50 // 51 // Examples: 52 // 53 // ValidateIdentifiers(identifiers, ValidArgumentBlockNumber | ValidArgumentDate, 0) 54 // ValidateIdentifiers(identifiers, ValidArgumentRange, 1) 55 // 56 // This routine can be used for both block identifiers and transaction 57 func ValidateIdentifiers(chain string, ids []string, validTypes ValidArgumentType, maxRanges int, results *[]identifiers.Identifier) error { 58 // A helper function to check if bitmask is set 59 isBitmaskSet := func(flag ValidArgumentType) bool { 60 return validTypes&flag != 0 61 } 62 63 if isBitmaskSet(ValidTransId) && isBitmaskSet(ValidBlockId) { 64 logger.Fatal("should not happen ==> both block ids and transaction ids appear in the same command.") 65 } 66 67 rangesFound := 0 68 for _, identifier := range ids { 69 if isBitmaskSet(ValidArgumentBlockHash) && IsBlockHash(identifier) { 70 appendBlockId(results, identifier) 71 continue 72 } 73 74 validTimestamp, _ := IsTimestamp(identifier) 75 if isBitmaskSet(ValidArgumentTimestamp) && validTimestamp { 76 appendBlockId(results, identifier) 77 continue 78 } 79 80 validBlockNumber, _ := IsBlockNumber(identifier) 81 if isBitmaskSet(ValidArgumentBlockNumber) && validBlockNumber { 82 appendBlockId(results, identifier) 83 continue 84 } 85 86 if isBitmaskSet(ValidArgumentDate) && IsDateTimeString(identifier) { 87 if isBeforeFirstBlock(chain, identifier) { 88 return &InvalidIdentifierLiteralError{ 89 Value: identifier, 90 Msg: "is before the first block.", 91 } 92 } 93 appendBlockId(results, identifier) 94 continue 95 } 96 97 if isBitmaskSet(ValidArgumentSpecialBlock) && tslib.IsSpecialBlock(chain, identifier) { 98 appendBlockId(results, identifier) 99 continue 100 } 101 102 if isBitmaskSet(ValidArgumentTransHash) && IsTransHash(identifier) { 103 appendTxId(results, identifier) 104 continue 105 } 106 107 if isBitmaskSet(ValidArgumentTransBlockNumberAndId) && IsTransBlockNumAndId(identifier) { 108 appendTxId(results, identifier) 109 continue 110 } 111 112 if isBitmaskSet(ValidArgumentTransBlockHashAndId) && IsTransBlockHashAndId(identifier) { 113 appendTxId(results, identifier) 114 continue 115 } 116 117 // If we are at this point and we don't want a range, it means that 118 // an identifier was invalid 119 if !isBitmaskSet(ValidArgumentRange) { 120 return &InvalidIdentifierLiteralError{ 121 Value: identifier, 122 } 123 } 124 125 _, rangeErr := IsRange(chain, identifier) 126 if rangeErr != nil { 127 return rangeErr 128 } 129 130 rangesFound++ 131 if rangesFound > maxRanges { 132 return ErrTooManyRanges 133 } 134 appendBlockId(results, identifier) 135 } 136 137 return nil 138 } 139 140 func appendBlockId(results *[]identifiers.Identifier, identifier string) { 141 if results == nil { 142 return 143 } 144 br, _ := identifiers.NewBlockRange(identifier) 145 if br != nil { 146 *results = append(*results, *br) 147 } 148 } 149 150 func appendTxId(results *[]identifiers.Identifier, identifier string) { 151 if results == nil { 152 return 153 } 154 155 // so the parser works for both transaction identifiers and block identifiers 156 identifier = strings.Replace(identifier, ".", "-", -1) 157 158 // so the parser works for transaction identifiers with blockHashOrNumber.* 159 if strings.Count(identifier, "-") == 1 && strings.HasSuffix(identifier, "-*") { 160 parts := strings.Split(identifier, "-") 161 identifier = parts[0] + "-0:all" 162 } 163 164 br, _ := identifiers.NewTxRange(identifier) 165 if br != nil { 166 if br.StartType == identifiers.BlockHash && br.EndType == identifiers.NotDefined { 167 // We can't really distinguish hashes in the parser, but if there is 168 // a single hash and nothing following the separator and we know we're 169 // looking for transactions, this is a transaction hash 170 br.StartType = identifiers.TransactionHash 171 } 172 br.EndType = identifiers.TransactionIndex 173 *results = append(*results, *br) 174 } 175 }