github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/tslib/list.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 tslib
     6  
     7  import (
     8  	"encoding/csv"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  
    16  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    17  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    18  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    19  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    20  )
    21  
    22  // GetSpecials returns a chain-specific list of special block names and numbers
    23  func GetSpecials(chain string) (specials []types.NamedBlock, err error) {
    24  	path := filepath.Join(config.MustGetPathToChainConfig(chain), "specials.csv")
    25  	_, err = os.Stat(path)
    26  	if err != nil {
    27  		// It's okay if there are no specials for a certain chain
    28  		if chain == "mainnet" {
    29  			// should not happen, but we want to know if it does
    30  			logger.Info("No special block file found for chain", chain)
    31  		}
    32  		return specials, nil
    33  	}
    34  
    35  	if specials, err = readSpecials(path, 6); err != nil {
    36  		if errors.Is(err, csv.ErrFieldCount) {
    37  			if specials, err = readSpecials(path, 4); err != nil {
    38  				return specials, err
    39  			}
    40  		}
    41  	}
    42  
    43  	if len(specials) == 0 {
    44  		err = fmt.Errorf("found no special blocks in file %s", path)
    45  	}
    46  
    47  	return
    48  }
    49  
    50  // IsSpecialBlock returns true if the given chain-specific name is a special block
    51  func IsSpecialBlock(chain, needle string) bool {
    52  	_, err := FromNameToBn(chain, needle)
    53  	return err == nil
    54  }
    55  
    56  func readSpecials(path string, nFields int) (specials []types.NamedBlock, err error) {
    57  	file, err := os.OpenFile(path, os.O_RDONLY, 0)
    58  	if err != nil {
    59  		return
    60  	}
    61  
    62  	reader := csv.NewReader(file)
    63  	reader.FieldsPerRecord = nFields
    64  	for {
    65  		if record, err := reader.Read(); err == io.EOF {
    66  			break
    67  
    68  		} else if err != nil {
    69  			return specials, err
    70  
    71  		} else {
    72  			if nFields == len(record) {
    73  				locs := map[string]int{
    74  					"bn":        1,
    75  					"name":      2,
    76  					"ts":        3,
    77  					"component": 0,
    78  					// "date":        4, // skipped since we have timestamp
    79  					"description": 5,
    80  				}
    81  				if nFields == 4 {
    82  					locs = map[string]int{
    83  						"bn":   0,
    84  						"name": 1,
    85  						"ts":   2,
    86  						// "date": 3, // skipped since we have timestamp
    87  					}
    88  				}
    89  				if strings.Contains(record[locs["bn"]], "-") {
    90  					// before block zero
    91  					continue
    92  				}
    93  				s := types.NamedBlock{
    94  					BlockNumber: base.MustParseBlknum(record[locs["bn"]]),
    95  					Timestamp:   base.MustParseTimestamp(record[locs["ts"]]),
    96  					Name:        record[locs["name"]],
    97  				}
    98  				// is this the header?
    99  				if s.BlockNumber == 0 && s.Name == "name" {
   100  					continue
   101  				}
   102  
   103  				if nFields == 6 {
   104  					s.Component = record[locs["component"]]
   105  					s.Description = record[locs["description"]]
   106  				} else {
   107  					s.Component = "execution"
   108  				}
   109  
   110  				specials = append(specials, s)
   111  			}
   112  		}
   113  	}
   114  	return
   115  }