245 lines
6.8 KiB
Go
245 lines
6.8 KiB
Go
package balance
|
|
|
|
import (
|
|
"crypto/elliptic"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/nns"
|
|
morphUtil "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/util"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt"
|
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
type accBalancePair struct {
|
|
scriptHash util.Uint160
|
|
balance *big.Int
|
|
}
|
|
|
|
const (
|
|
dumpBalancesStorageFlag = "storage"
|
|
dumpBalancesAlphabetFlag = "alphabet"
|
|
dumpBalancesProxyFlag = "proxy"
|
|
dumpBalancesUseScriptHashFlag = "script-hash"
|
|
)
|
|
|
|
func dumpBalances(cmd *cobra.Command, _ []string) error {
|
|
var (
|
|
dumpStorage, _ = cmd.Flags().GetBool(dumpBalancesStorageFlag)
|
|
dumpAlphabet, _ = cmd.Flags().GetBool(dumpBalancesAlphabetFlag)
|
|
dumpProxy, _ = cmd.Flags().GetBool(dumpBalancesProxyFlag)
|
|
nnsCs *state.Contract
|
|
nmHash util.Uint160
|
|
)
|
|
|
|
c, err := morphUtil.GetN3Client(viper.GetViper())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
inv := invoker.New(c, nil)
|
|
|
|
if dumpStorage || dumpAlphabet || dumpProxy {
|
|
r := management.NewReader(inv)
|
|
nnsCs, err = r.GetContractByID(1)
|
|
if err != nil {
|
|
return fmt.Errorf("can't get NNS contract info: %w", err)
|
|
}
|
|
|
|
nmHash, err = morphUtil.NNSResolveHash(inv, nnsCs.Hash, morphUtil.DomainOf(morphUtil.NetmapContract))
|
|
if err != nil {
|
|
return fmt.Errorf("can't get netmap contract hash: %w", err)
|
|
}
|
|
}
|
|
|
|
irList, err := fetchIRNodes(c, rolemgmt.Hash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := fetchBalances(inv, gas.Hash, irList); err != nil {
|
|
return err
|
|
}
|
|
printBalances(cmd, "Inner ring nodes balances:", irList)
|
|
|
|
if dumpStorage {
|
|
if err := printStorageNodeBalances(cmd, inv, nmHash); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if dumpProxy {
|
|
if err := printProxyContractBalance(cmd, inv, nnsCs.Hash); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if dumpAlphabet {
|
|
if err := printAlphabetContractBalances(cmd, c, inv, len(irList), nnsCs.Hash); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func printStorageNodeBalances(cmd *cobra.Command, inv *invoker.Invoker, nmHash util.Uint160) error {
|
|
arr, err := unwrap.Array(inv.Call(nmHash, "netmap"))
|
|
if err != nil {
|
|
return errors.New("can't fetch the list of storage nodes")
|
|
}
|
|
|
|
snList := make([]accBalancePair, len(arr))
|
|
for i := range arr {
|
|
node, ok := arr[i].Value().([]stackitem.Item)
|
|
if !ok || len(node) == 0 {
|
|
return errors.New("can't parse the list of storage nodes")
|
|
}
|
|
bs, err := node[0].TryBytes()
|
|
if err != nil {
|
|
return errors.New("can't parse the list of storage nodes")
|
|
}
|
|
var ni netmap.NodeInfo
|
|
if err := ni.Unmarshal(bs); err != nil {
|
|
return fmt.Errorf("can't parse the list of storage nodes: %w", err)
|
|
}
|
|
pub, err := keys.NewPublicKeyFromBytes(ni.PublicKey(), elliptic.P256())
|
|
if err != nil {
|
|
return fmt.Errorf("can't parse storage node public key: %w", err)
|
|
}
|
|
snList[i].scriptHash = pub.GetScriptHash()
|
|
}
|
|
|
|
if err := fetchBalances(inv, gas.Hash, snList); err != nil {
|
|
return err
|
|
}
|
|
|
|
printBalances(cmd, "\nStorage node balances:", snList)
|
|
return nil
|
|
}
|
|
|
|
func printProxyContractBalance(cmd *cobra.Command, inv *invoker.Invoker, nnsHash util.Uint160) error {
|
|
h, err := morphUtil.NNSResolveHash(inv, nnsHash, morphUtil.DomainOf(morphUtil.ProxyContract))
|
|
if err != nil {
|
|
return fmt.Errorf("can't get hash of the proxy contract: %w", err)
|
|
}
|
|
|
|
proxyList := []accBalancePair{{scriptHash: h}}
|
|
if err := fetchBalances(inv, gas.Hash, proxyList); err != nil {
|
|
return err
|
|
}
|
|
|
|
printBalances(cmd, "\nProxy contract balance:", proxyList)
|
|
return nil
|
|
}
|
|
|
|
func printAlphabetContractBalances(cmd *cobra.Command, c morphUtil.Client, inv *invoker.Invoker, count int, nnsHash util.Uint160) error {
|
|
alphaList := make([]accBalancePair, count)
|
|
|
|
w := io.NewBufBinWriter()
|
|
for i := range alphaList {
|
|
emit.AppCall(w.BinWriter, nnsHash, "resolve", callflag.ReadOnly,
|
|
morphUtil.GetAlphabetNNSDomain(i),
|
|
int64(nns.TXT))
|
|
}
|
|
if w.Err != nil {
|
|
panic(w.Err)
|
|
}
|
|
|
|
alphaRes, err := c.InvokeScript(w.Bytes(), nil)
|
|
if err != nil {
|
|
return fmt.Errorf("can't fetch info from NNS: %w", err)
|
|
}
|
|
|
|
for i := range alphaList {
|
|
h, err := morphUtil.ParseNNSResolveResult(alphaRes.Stack[i])
|
|
if err != nil {
|
|
return fmt.Errorf("can't fetch the alphabet contract #%d hash: %w", i, err)
|
|
}
|
|
alphaList[i].scriptHash = h
|
|
}
|
|
|
|
if err := fetchBalances(inv, gas.Hash, alphaList); err != nil {
|
|
return err
|
|
}
|
|
|
|
printBalances(cmd, "\nAlphabet contracts balances:", alphaList)
|
|
return nil
|
|
}
|
|
|
|
func fetchIRNodes(c morphUtil.Client, desigHash util.Uint160) ([]accBalancePair, error) {
|
|
inv := invoker.New(c, nil)
|
|
|
|
height, err := c.GetBlockCount()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("can't get block height: %w", err)
|
|
}
|
|
|
|
arr, err := morphUtil.GetDesignatedByRole(inv, desigHash, noderoles.NeoFSAlphabet, height)
|
|
if err != nil {
|
|
return nil, errors.New("can't fetch list of IR nodes from the netmap contract")
|
|
}
|
|
|
|
irList := make([]accBalancePair, len(arr))
|
|
for i := range arr {
|
|
irList[i].scriptHash = arr[i].GetScriptHash()
|
|
}
|
|
return irList, nil
|
|
}
|
|
|
|
func printBalances(cmd *cobra.Command, prefix string, accounts []accBalancePair) {
|
|
useScriptHash, _ := cmd.Flags().GetBool(dumpBalancesUseScriptHashFlag)
|
|
|
|
cmd.Println(prefix)
|
|
for i := range accounts {
|
|
var addr string
|
|
if useScriptHash {
|
|
addr = accounts[i].scriptHash.StringLE()
|
|
} else {
|
|
addr = address.Uint160ToString(accounts[i].scriptHash)
|
|
}
|
|
cmd.Printf("%s: %s\n", addr, fixedn.ToString(accounts[i].balance, 8))
|
|
}
|
|
}
|
|
|
|
func fetchBalances(c *invoker.Invoker, gasHash util.Uint160, accounts []accBalancePair) error {
|
|
w := io.NewBufBinWriter()
|
|
for i := range accounts {
|
|
emit.AppCall(w.BinWriter, gasHash, "balanceOf", callflag.ReadStates, accounts[i].scriptHash)
|
|
}
|
|
if w.Err != nil {
|
|
panic(w.Err)
|
|
}
|
|
|
|
res, err := c.Run(w.Bytes())
|
|
if err != nil || res.State != vmstate.Halt.String() || len(res.Stack) != len(accounts) {
|
|
return errors.New("can't fetch account balances")
|
|
}
|
|
|
|
for i := range accounts {
|
|
bal, err := res.Stack[i].TryInteger()
|
|
if err != nil {
|
|
return fmt.Errorf("can't parse account balance: %w", err)
|
|
}
|
|
accounts[i].balance = bal
|
|
}
|
|
return nil
|
|
}
|