2023-09-27 11:05:46 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
endpointFlagKey = "rpc-endpoint"
|
|
|
|
fromFlagKey = "from"
|
|
|
|
toFlagKey = "to"
|
|
|
|
notificationFlagKey = "notification"
|
|
|
|
cacheFlagKey = "cache"
|
|
|
|
workersFlagKey = "workers"
|
|
|
|
disableProgressBarFlagKey = "disable-progress-bar"
|
|
|
|
stutterThresholdFlagKey = "threshold"
|
2023-10-19 14:20:45 +00:00
|
|
|
forceCacheRewriteKey = "force"
|
2023-09-27 11:05:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
endpointFlag = &cli.StringFlag{
|
|
|
|
Name: endpointFlagKey,
|
|
|
|
Aliases: []string{"r"},
|
|
|
|
Usage: "N3 RPC endpoint",
|
|
|
|
Required: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
fromFlag = &cli.StringFlag{
|
|
|
|
Name: fromFlagKey,
|
|
|
|
Usage: "starting block (can be relative value with minus prefix, e.g. 'm100')",
|
|
|
|
Required: true,
|
|
|
|
Value: "",
|
|
|
|
}
|
|
|
|
|
|
|
|
toFlag = &cli.StringFlag{
|
|
|
|
Name: toFlagKey,
|
|
|
|
Usage: "ending block (can be relative value with plus prefix, e.g. 'p100' or omitted for latest block in chain)",
|
|
|
|
Required: false,
|
|
|
|
Value: "",
|
|
|
|
}
|
|
|
|
|
|
|
|
notificationFlag = &cli.StringSliceFlag{
|
|
|
|
Name: notificationFlagKey,
|
|
|
|
Aliases: []string{"n"},
|
|
|
|
Usage: "'notification:contract' pair (specify LE script hash, '*' for any contract or 'gas' and 'neo' strings)",
|
|
|
|
Required: true,
|
|
|
|
Value: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
cacheFlag = &cli.StringFlag{
|
|
|
|
Name: cacheFlagKey,
|
|
|
|
Aliases: []string{"c"},
|
|
|
|
Usage: "path to the blockchain cache (default: $HOME/.config/monza)",
|
|
|
|
Value: "",
|
|
|
|
}
|
|
|
|
|
|
|
|
workersFlag = &cli.Uint64Flag{
|
|
|
|
Name: workersFlagKey,
|
|
|
|
Aliases: []string{"w"},
|
|
|
|
Usage: "amount of workers for parallel block fetch",
|
|
|
|
Value: 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
disableProgressBarFlag = &cli.BoolFlag{
|
|
|
|
Name: disableProgressBarFlagKey,
|
|
|
|
Usage: "disable progress bar output",
|
|
|
|
}
|
|
|
|
|
|
|
|
stutterThresholdFlag = &cli.DurationFlag{
|
|
|
|
Name: stutterThresholdFlagKey,
|
|
|
|
Aliases: []string{"t"},
|
|
|
|
Usage: "duration limit between block timestamps",
|
|
|
|
Value: 20 * time.Second,
|
|
|
|
}
|
2023-10-19 14:20:45 +00:00
|
|
|
|
|
|
|
forceCacheRewriteFlag = &cli.BoolFlag{
|
|
|
|
Name: forceCacheRewriteKey,
|
|
|
|
Aliases: []string{"f"},
|
|
|
|
Usage: "force blockchain cache rewrite",
|
|
|
|
}
|
2023-09-27 11:05:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func parseNotifications(notifications []string, cli *rpcclient.Client) (map[string]*util.Uint160, error) {
|
|
|
|
res := make(map[string]*util.Uint160, len(notifications))
|
|
|
|
|
|
|
|
for _, n := range notifications {
|
|
|
|
pair := strings.Split(n, ":")
|
|
|
|
if len(pair) != 2 {
|
|
|
|
return nil, fmt.Errorf("invalid notification %s", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
name := pair[0]
|
|
|
|
|
|
|
|
switch contractName := strings.ToLower(pair[1]); contractName {
|
|
|
|
case "*":
|
|
|
|
res[name] = nil
|
|
|
|
case "gas":
|
|
|
|
state, err := cli.GetContractStateByAddressOrName(nativenames.Gas)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid contract name %s", contractName)
|
|
|
|
}
|
|
|
|
res[name] = &state.Hash
|
|
|
|
case "neo":
|
|
|
|
state, err := cli.GetContractStateByAddressOrName(nativenames.Neo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid contract name %s", contractName)
|
|
|
|
}
|
|
|
|
res[name] = &state.Hash
|
|
|
|
default:
|
|
|
|
u160, err := util.Uint160DecodeStringLE(contractName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid contract name %s", contractName)
|
|
|
|
}
|
|
|
|
res[name] = &u160
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseInterval(fromStr, toStr string, cli *rpcclient.Client) (from, to uint32, err error) {
|
|
|
|
switch { // parse from value and return result if it is relative
|
|
|
|
case len(fromStr) == 0:
|
|
|
|
return 0, 0, ErrInvalidInterval(fromStr, toStr)
|
|
|
|
case fromStr[0] == 'm':
|
|
|
|
v, err := strconv.Atoi(fromStr[1:])
|
|
|
|
if err != nil || v <= 0 {
|
|
|
|
return 0, 0, ErrInvalidInterval(fromStr, toStr)
|
|
|
|
}
|
|
|
|
h, err := cli.GetBlockCount()
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, fmt.Errorf("latest block index unavailable: %w", err)
|
|
|
|
}
|
|
|
|
if uint32(v) >= h {
|
|
|
|
return 0, 0, fmt.Errorf("latest block is less than from value, from:%s, to:%d", fromStr, h)
|
|
|
|
}
|
|
|
|
return h - uint32(v), h, nil
|
|
|
|
default:
|
|
|
|
v, err := strconv.Atoi(fromStr)
|
|
|
|
if err != nil || v <= 0 {
|
|
|
|
return 0, 0, ErrInvalidInterval(fromStr, toStr)
|
|
|
|
}
|
|
|
|
from = uint32(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch { // parse to value
|
|
|
|
case len(toStr) == 0:
|
|
|
|
h, err := cli.GetBlockCount()
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, fmt.Errorf("latest block index unavailable: %w", err)
|
|
|
|
}
|
|
|
|
if h <= from {
|
|
|
|
return 0, 0, fmt.Errorf("latest block is less than from value, from:%d, to:%d", from, h)
|
|
|
|
}
|
|
|
|
return from, h, nil
|
|
|
|
case toStr[0] == 'p':
|
|
|
|
v, err := strconv.Atoi(toStr[1:])
|
|
|
|
if err != nil || v <= 0 {
|
|
|
|
return 0, 0, ErrInvalidInterval(fromStr, toStr)
|
|
|
|
}
|
|
|
|
return from, from + uint32(v), nil
|
|
|
|
default:
|
|
|
|
v, err := strconv.Atoi(toStr)
|
|
|
|
if err != nil || v <= 0 {
|
|
|
|
return 0, 0, ErrInvalidInterval(fromStr, toStr)
|
|
|
|
}
|
|
|
|
if uint32(v) <= from {
|
|
|
|
return 0, 0, ErrInvalidInterval(fromStr, toStr)
|
|
|
|
}
|
|
|
|
return from, uint32(v), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ErrInvalidInterval(from, to string) error {
|
|
|
|
return fmt.Errorf("invalid block interval from:%s to:%s", from, to)
|
|
|
|
}
|