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  }