github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/internal/chunks/options.go (about)

     1  // Copyright 2016, 2024 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   * Parts of this file were auto generated. Edit only those parts of
     6   * the code inside of 'EXISTING_CODE' tags.
     7   */
     8  
     9  package chunksPkg
    10  
    11  import (
    12  	// EXISTING_CODE
    13  	"encoding/json"
    14  	"io"
    15  	"net/http"
    16  	"net/url"
    17  	"os"
    18  	"strings"
    19  
    20  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/internal/globals"
    21  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base"
    22  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/caps"
    23  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config"
    24  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file"
    25  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/identifiers"
    26  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/index"
    27  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger"
    28  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc"
    29  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/tslib"
    30  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types"
    31  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/validate"
    32  	"github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk"
    33  	"github.com/ethereum/go-ethereum/common/hexutil"
    34  	// EXISTING_CODE
    35  )
    36  
    37  // ChunksOptions provides all command options for the chifra chunks command.
    38  type ChunksOptions struct {
    39  	Mode       string                   `json:"mode,omitempty"`       // The type of data to process
    40  	Blocks     []string                 `json:"blocks,omitempty"`     // An optional list of blocks to intersect with chunk ranges
    41  	BlockIds   []identifiers.Identifier `json:"blockIds,omitempty"`   // Block identifiers
    42  	Check      bool                     `json:"check,omitempty"`      // Check the manifest, index, or blooms for internal consistency
    43  	Pin        bool                     `json:"pin,omitempty"`        // Pin the manifest or each index chunk and bloom
    44  	Publish    bool                     `json:"publish,omitempty"`    // Publish the manifest to the Unchained Index smart contract
    45  	Publisher  string                   `json:"publisher,omitempty"`  // For some query options, the publisher of the index
    46  	Truncate   base.Blknum              `json:"truncate,omitempty"`   // Truncate the entire index at this block (requires a block identifier)
    47  	Remote     bool                     `json:"remote,omitempty"`     // Prior to processing, retrieve the manifest from the Unchained Index smart contract
    48  	Belongs    []string                 `json:"belongs,omitempty"`    // In index mode only, checks the address(es) for inclusion in the given index chunk
    49  	Diff       bool                     `json:"diff,omitempty"`       // Compare two index portions (see notes)
    50  	FirstBlock base.Blknum              `json:"firstBlock,omitempty"` // First block to process (inclusive)
    51  	LastBlock  base.Blknum              `json:"lastBlock,omitempty"`  // Last block to process (inclusive)
    52  	MaxAddrs   uint64                   `json:"maxAddrs,omitempty"`   // The max number of addresses to process in a given chunk
    53  	Deep       bool                     `json:"deep,omitempty"`       // If true, dig more deeply during checking (manifest only)
    54  	Rewrite    bool                     `json:"rewrite,omitempty"`    // For the --pin --deep mode only, writes the manifest back to the index folder (see notes)
    55  	List       bool                     `json:"list,omitempty"`       // For the pins mode only, list the remote pins
    56  	Unpin      bool                     `json:"unpin,omitempty"`      // For the pins mode only, if true reads local ./unpins file for valid CIDs and remotely unpins each (skips non-CIDs)
    57  	Count      bool                     `json:"count,omitempty"`      // For certain modes only, display the count of records
    58  	Tag        string                   `json:"tag,omitempty"`        // Visits each chunk and updates the headers with the supplied version string (vX.Y.Z-str)
    59  	Sleep      float64                  `json:"sleep,omitempty"`      // For --remote pinning only, seconds to sleep between API calls
    60  	Globals    globals.GlobalOptions    `json:"globals,omitempty"`    // The global options
    61  	Conn       *rpc.Connection          `json:"conn,omitempty"`       // The connection to the RPC server
    62  	BadFlag    error                    `json:"badFlag,omitempty"`    // An error flag if needed
    63  	// EXISTING_CODE
    64  	PublisherAddr base.Address `json:"-"`
    65  	// EXISTING_CODE
    66  }
    67  
    68  var defaultChunksOptions = ChunksOptions{
    69  	Truncate:  base.NOPOSN,
    70  	LastBlock: base.NOPOSN,
    71  	MaxAddrs:  base.NOPOS,
    72  }
    73  
    74  // testLog is used only during testing to export the options for this test case.
    75  func (opts *ChunksOptions) testLog() {
    76  	logger.TestLog(len(opts.Mode) > 0, "Mode: ", opts.Mode)
    77  	logger.TestLog(len(opts.Blocks) > 0, "Blocks: ", opts.Blocks)
    78  	logger.TestLog(opts.Check, "Check: ", opts.Check)
    79  	logger.TestLog(opts.Pin, "Pin: ", opts.Pin)
    80  	logger.TestLog(opts.Publish, "Publish: ", opts.Publish)
    81  	logger.TestLog(len(opts.Publisher) > 0, "Publisher: ", opts.Publisher)
    82  	logger.TestLog(opts.Truncate != base.NOPOSN, "Truncate: ", opts.Truncate)
    83  	logger.TestLog(opts.Remote, "Remote: ", opts.Remote)
    84  	logger.TestLog(len(opts.Belongs) > 0, "Belongs: ", opts.Belongs)
    85  	logger.TestLog(opts.Diff, "Diff: ", opts.Diff)
    86  	logger.TestLog(opts.FirstBlock != 0, "FirstBlock: ", opts.FirstBlock)
    87  	logger.TestLog(opts.LastBlock != base.NOPOSN && opts.LastBlock != 0, "LastBlock: ", opts.LastBlock)
    88  	logger.TestLog(opts.MaxAddrs != base.NOPOS, "MaxAddrs: ", opts.MaxAddrs)
    89  	logger.TestLog(opts.Deep, "Deep: ", opts.Deep)
    90  	logger.TestLog(opts.Rewrite, "Rewrite: ", opts.Rewrite)
    91  	logger.TestLog(opts.List, "List: ", opts.List)
    92  	logger.TestLog(opts.Unpin, "Unpin: ", opts.Unpin)
    93  	logger.TestLog(opts.Count, "Count: ", opts.Count)
    94  	logger.TestLog(len(opts.Tag) > 0, "Tag: ", opts.Tag)
    95  	logger.TestLog(opts.Sleep != float64(0.0), "Sleep: ", opts.Sleep)
    96  	opts.Conn.TestLog(opts.getCaches())
    97  	opts.Globals.TestLog()
    98  }
    99  
   100  // String implements the Stringer interface
   101  func (opts *ChunksOptions) String() string {
   102  	b, _ := json.MarshalIndent(opts, "", "  ")
   103  	return string(b)
   104  }
   105  
   106  // chunksFinishParseApi finishes the parsing for server invocations. Returns a new ChunksOptions.
   107  func chunksFinishParseApi(w http.ResponseWriter, r *http.Request) *ChunksOptions {
   108  	values := r.URL.Query()
   109  	if r.Header.Get("User-Agent") == "testRunner" {
   110  		values.Set("testRunner", "true")
   111  	}
   112  	return ChunksFinishParseInternal(w, values)
   113  }
   114  
   115  func ChunksFinishParseInternal(w io.Writer, values url.Values) *ChunksOptions {
   116  	copy := defaultChunksOptions
   117  	copy.Globals.Caps = getCaps()
   118  	opts := &copy
   119  	opts.Truncate = base.NOPOSN
   120  	opts.LastBlock = base.NOPOSN
   121  	opts.MaxAddrs = base.NOPOS
   122  	for key, value := range values {
   123  		switch key {
   124  		case "mode":
   125  			opts.Mode = value[0]
   126  		case "blocks":
   127  			for _, val := range value {
   128  				s := strings.Split(val, " ") // may contain space separated items
   129  				opts.Blocks = append(opts.Blocks, s...)
   130  			}
   131  		case "check":
   132  			opts.Check = true
   133  		case "pin":
   134  			opts.Pin = true
   135  		case "publish":
   136  			opts.Publish = true
   137  		case "publisher":
   138  			opts.Publisher = value[0]
   139  		case "truncate":
   140  			opts.Truncate = base.MustParseBlknum(value[0])
   141  		case "remote":
   142  			opts.Remote = true
   143  		case "belongs":
   144  			for _, val := range value {
   145  				s := strings.Split(val, " ") // may contain space separated items
   146  				opts.Belongs = append(opts.Belongs, s...)
   147  			}
   148  		case "diff":
   149  			opts.Diff = true
   150  		case "firstBlock":
   151  			opts.FirstBlock = base.MustParseBlknum(value[0])
   152  		case "lastBlock":
   153  			opts.LastBlock = base.MustParseBlknum(value[0])
   154  		case "maxAddrs":
   155  			opts.MaxAddrs = base.MustParseUint64(value[0])
   156  		case "deep":
   157  			opts.Deep = true
   158  		case "rewrite":
   159  			opts.Rewrite = true
   160  		case "list":
   161  			opts.List = true
   162  		case "unpin":
   163  			opts.Unpin = true
   164  		case "count":
   165  			opts.Count = true
   166  		case "tag":
   167  			opts.Tag = value[0]
   168  		case "sleep":
   169  			opts.Sleep = base.MustParseFloat64(value[0])
   170  		default:
   171  			if !copy.Globals.Caps.HasKey(key) {
   172  				err := validate.Usage("Invalid key ({0}) in {1} route.", key, "chunks")
   173  				if opts.BadFlag == nil || opts.BadFlag.Error() > err.Error() {
   174  					opts.BadFlag = err
   175  				}
   176  			}
   177  		}
   178  	}
   179  	opts.Conn = opts.Globals.FinishParseApi(w, values, opts.getCaches())
   180  	opts.Publisher, _ = opts.Conn.GetEnsAddress(config.GetPublisher(opts.Publisher))
   181  	opts.PublisherAddr = base.HexToAddress(opts.Publisher)
   182  
   183  	// EXISTING_CODE
   184  	// EXISTING_CODE
   185  	opts.Belongs, _ = opts.Conn.GetEnsAddresses(opts.Belongs)
   186  
   187  	return opts
   188  }
   189  
   190  // chunksFinishParse finishes the parsing for command line invocations. Returns a new ChunksOptions.
   191  func chunksFinishParse(args []string) *ChunksOptions {
   192  	// remove duplicates from args if any (not needed in api mode because the server does it).
   193  	dedup := map[string]int{}
   194  	if len(args) > 0 {
   195  		tmp := []string{}
   196  		for _, arg := range args {
   197  			if value := dedup[arg]; value == 0 {
   198  				tmp = append(tmp, arg)
   199  			}
   200  			dedup[arg]++
   201  		}
   202  		args = tmp
   203  	}
   204  
   205  	defFmt := "txt"
   206  	opts := GetOptions()
   207  	opts.Conn = opts.Globals.FinishParse(args, opts.getCaches())
   208  	opts.Publisher, _ = opts.Conn.GetEnsAddress(config.GetPublisher(opts.Publisher))
   209  	opts.PublisherAddr = base.HexToAddress(opts.Publisher)
   210  
   211  	// EXISTING_CODE
   212  	if len(args) > 0 {
   213  		opts.Mode = args[0]
   214  		for i, arg := range args {
   215  			if i > 0 {
   216  				if base.IsValidAddress(arg) {
   217  					opts.Belongs = append(opts.Belongs, arg)
   218  				} else {
   219  					opts.Blocks = append(opts.Blocks, arg)
   220  				}
   221  			}
   222  		}
   223  	}
   224  	if opts.Truncate == 0 {
   225  		opts.Truncate = base.NOPOSN
   226  	}
   227  	if opts.LastBlock == 0 {
   228  		opts.LastBlock = base.NOPOSN
   229  	}
   230  	if opts.MaxAddrs == 0 {
   231  		opts.MaxAddrs = base.NOPOS
   232  	}
   233  	getDef := func(def string) string {
   234  		if opts.Truncate != base.NOPOSN || len(opts.Belongs) > 0 || opts.Pin {
   235  			return "json"
   236  		}
   237  		return def
   238  	}
   239  	defFmt = getDef(defFmt)
   240  	// EXISTING_CODE
   241  	opts.Belongs, _ = opts.Conn.GetEnsAddresses(opts.Belongs)
   242  	if len(opts.Globals.Format) == 0 || opts.Globals.Format == "none" {
   243  		opts.Globals.Format = defFmt
   244  	}
   245  
   246  	return opts
   247  }
   248  
   249  func GetOptions() *ChunksOptions {
   250  	// EXISTING_CODE
   251  	// EXISTING_CODE
   252  	return &defaultChunksOptions
   253  }
   254  
   255  func getCaps() caps.Capability {
   256  	var capabilities caps.Capability // capabilities for chifra chunks
   257  	capabilities = capabilities.Add(caps.Default)
   258  	// EXISTING_CODE
   259  	// EXISTING_CODE
   260  	return capabilities
   261  }
   262  
   263  func ResetOptions(testMode bool) {
   264  	// We want to keep writer between command file calls
   265  	w := GetOptions().Globals.Writer
   266  	opts := ChunksOptions{}
   267  	globals.SetDefaults(&opts.Globals)
   268  	opts.Globals.TestMode = testMode
   269  	opts.Globals.Writer = w
   270  	opts.Globals.Caps = getCaps()
   271  	opts.Truncate = base.NOPOSN
   272  	opts.LastBlock = base.NOPOSN
   273  	opts.MaxAddrs = base.NOPOS
   274  	defaultChunksOptions = opts
   275  }
   276  
   277  func (opts *ChunksOptions) getCaches() (caches map[walk.CacheType]bool) {
   278  	// EXISTING_CODE
   279  	// EXISTING_CODE
   280  	return
   281  }
   282  
   283  // EXISTING_CODE
   284  func (opts *ChunksOptions) shouldShow(obj types.AddrRecord) bool {
   285  	if opts.Mode == "addresses" || opts.Mode == "appearances" {
   286  		return opts.Globals.Verbose
   287  	}
   288  
   289  	for _, addr := range opts.Belongs {
   290  		if hexutil.Encode(obj.Address.Bytes()) == addr {
   291  			return true
   292  		}
   293  	}
   294  	return false
   295  }
   296  
   297  func GetChunkStats(chain, path string) (s types.ChunkStats, err error) {
   298  	chunk, err := index.OpenChunk(path, true /* check */)
   299  	if err != nil && !os.IsNotExist(err) {
   300  		return s, err
   301  	}
   302  	defer chunk.Close()
   303  
   304  	rng := chunk.Range
   305  	s = types.ChunkStats{
   306  		Range:   rng.String(),
   307  		NBlocks: uint64(chunk.Range.Last - chunk.Range.First + 1),
   308  		NAddrs:  uint64(chunk.Index.Header.AddressCount),
   309  		NApps:   uint64(chunk.Index.Header.AppearanceCount),
   310  		NBlooms: uint64(chunk.Bloom.Count),
   311  		BloomSz: uint64(file.FileSize(index.ToBloomPath(path))),
   312  		ChunkSz: uint64(file.FileSize(index.ToIndexPath(path))),
   313  		RecWid:  4 + index.BLOOM_WIDTH_IN_BYTES,
   314  	}
   315  	rd := tslib.RangeToBounds(chain, &rng)
   316  	s.RangeDates = &rd
   317  
   318  	if s.NBlocks > 0 {
   319  		s.AddrsPerBlock = float64(s.NAddrs) / float64(s.NBlocks)
   320  		s.AppsPerBlock = float64(s.NApps) / float64(s.NBlocks)
   321  	}
   322  
   323  	if s.NAddrs > 0 {
   324  		s.AppsPerAddr = float64(s.NApps) / float64(s.NAddrs)
   325  	}
   326  
   327  	if s.BloomSz > 0 {
   328  		s.Ratio = float64(s.ChunkSz) / float64(s.BloomSz)
   329  	}
   330  
   331  	return s, nil
   332  }
   333  
   334  // EXISTING_CODE