Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 30e10b1

Browse files
committed
Add a verify balance function
1 parent 4d07d5c commit 30e10b1

11 files changed

Lines changed: 471 additions & 4 deletions

File tree

Utilities/snapshot/internal/cmd/clean.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"os"
66

7+
"github.com/FactomProject/factomd/Utilities/snapshot/internal"
8+
79
survey "github.com/AlecAivazis/survey/v2"
810
"github.com/sirupsen/logrus"
911
"github.com/spf13/cobra"
@@ -58,7 +60,7 @@ func cleanCmd() *cobra.Command {
5860
}
5961

6062
cmd.Flags().BoolVarP(&skipYes, "yes", "y", false, "skip yes prompt")
61-
cmd.Flags().StringVarP(&dumpDirectory, "dump-dir", "d", "snapshot", "where to dump snapshot data. empty means do not dump")
63+
cmd.Flags().StringVarP(&dumpDirectory, "dump-dir", "d", internal.DefaultSnapshotDir, "where to dump snapshot data. empty means do not dump")
6264

6365
return cmd
6466
}

Utilities/snapshot/internal/cmd/root.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strconv"
77
"time"
88

9+
"github.com/FactomProject/factomd/Utilities/snapshot/internal"
910
"github.com/FactomProject/factomd/Utilities/snapshot/internal/snapshot"
1011
"github.com/FactomProject/factomd/Utilities/tools"
1112
"github.com/sirupsen/logrus"
@@ -34,6 +35,7 @@ func RootCmd() *cobra.Command {
3435

3536
cmd.AddCommand(snapshotCmd())
3637
cmd.AddCommand(cleanCmd())
38+
cmd.AddCommand(verifyCmd())
3739
cmd.PersistentFlags().String("log", "debug", "set the log level")
3840

3941
return cmd
@@ -108,7 +110,7 @@ func snapshotCmd() *cobra.Command {
108110
cmd.Flags().StringVar(&dbPath, "db", "$HOME/.factom/m2/main-database/ldb/MAIN/factoid_level.db",
109111
"the location of the database to use")
110112
cmd.Flags().Var(&debugHeights, "debug-heights", "heights to print diagnostic information at")
111-
cmd.Flags().StringVarP(&dumpDirectory, "dump-dir", "d", "snapshot", "where to dump snapshot data. empty means do not dump")
113+
cmd.Flags().StringVarP(&dumpDirectory, "dump-dir", "d", internal.DefaultSnapshotDir, "where to dump snapshot data. empty means do not dump")
112114

113115
return cmd
114116
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/FactomProject/factomd/Utilities/snapshot/internal"
9+
10+
"github.com/FactomProject/factomd/Utilities/snapshot/internal/verify"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
func verifyCmd() *cobra.Command {
15+
cmd := &cobra.Command{
16+
Use: "verify",
17+
Short: "verifies the snapshotted data against factom",
18+
Long: "Verifying the data will compare the snapshot data to a mainnet factom node.",
19+
RunE: func(cmd *cobra.Command, args []string) error {
20+
return cmd.Help()
21+
},
22+
}
23+
24+
cmd.PersistentFlags().StringP("dump-dir", "d", internal.DefaultSnapshotDir, "where to dump snapshot data. empty means do not dump")
25+
cmd.AddCommand(verifyBalances())
26+
return cmd
27+
}
28+
29+
func verifyBalances() *cobra.Command {
30+
var (
31+
apiAddr string
32+
apiType string
33+
)
34+
35+
cmd := &cobra.Command{
36+
Use: "balances",
37+
Short: "verify found balances",
38+
RunE: func(cmd *cobra.Command, args []string) error {
39+
ctx := cmd.Context()
40+
dumpDirectory, err := cmd.Flags().GetString("dump-dir")
41+
if err != nil {
42+
return err
43+
}
44+
45+
log, err := logger(cmd)
46+
if err != nil {
47+
return err
48+
}
49+
50+
fp := filepath.Join(dumpDirectory, internal.DefaultBalanceFile)
51+
file, err := os.OpenFile(fp, os.O_RDONLY, 0777)
52+
if err != nil {
53+
return fmt.Errorf("open file %s: %w", fp, err)
54+
}
55+
defer file.Close()
56+
switch apiType {
57+
case "factomd":
58+
err := verify.VerifyBalancesAgainstFactomd(ctx, log, apiAddr, file)
59+
if err != nil {
60+
return fmt.Errorf("verify: %w", err)
61+
}
62+
case "factom.pro":
63+
err := verify.VerifyBalancesAgainstFactomPro(ctx, log, apiAddr, file)
64+
if err != nil {
65+
return fmt.Errorf("verify: %w", err)
66+
}
67+
default:
68+
return fmt.Errorf("api type %s is not supported", apiType)
69+
}
70+
71+
return nil
72+
},
73+
}
74+
75+
cmd.Flags().StringVarP(&apiAddr, "api-addr", "s", "localhost:8088", "address of the api server to verify against")
76+
cmd.Flags().StringVar(&apiType, "api-type", "factomd", "supports 'factomd', and 'factom.pro'")
77+
78+
return cmd
79+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package internal
2+
3+
const (
4+
DefaultSnapshotDir = "snapshot"
5+
DefaultBalanceFile = "balances"
6+
DefaultChainDir = "chains"
7+
)

Utilities/snapshot/internal/snapshot/snapshot.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"path/filepath"
88

9+
"github.com/FactomProject/factomd/Utilities/snapshot/internal"
10+
911
"github.com/FactomProject/factomd/database/databaseOverlay"
1012
"github.com/sirupsen/logrus"
1113
)
@@ -37,7 +39,7 @@ func New(cfg Config) (*Snapshotter, error) {
3739
db: cfg.DB,
3840
debugHeights: cfg.DebugHeights,
3941
balances: newBalanceSnapshot(),
40-
entries: NewEntrySnapshot(filepath.Join(cfg.DumpDir, "chains")),
42+
entries: NewEntrySnapshot(filepath.Join(cfg.DumpDir, internal.DefaultChainDir)),
4143
stop: cfg.Stop,
4244
dumpDir: cfg.DumpDir,
4345
recordEntries: cfg.RecordEntries,

Utilities/snapshot/internal/snapshot/walk.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"path/filepath"
77
"time"
88

9+
"github.com/FactomProject/factomd/Utilities/snapshot/internal"
10+
911
"github.com/sirupsen/logrus"
1012
)
1113

@@ -40,7 +42,7 @@ func (s *Snapshotter) Dump() error {
4042
}
4143

4244
// Dump balances
43-
balPath := filepath.Join(s.dumpDir, "balances")
45+
balPath := filepath.Join(s.dumpDir, internal.DefaultBalanceFile)
4446
file, err := os.OpenFile(balPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0766)
4547
if err != nil {
4648
return fmt.Errorf("open %s: %w", balPath, err)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package api
2+
3+
import "context"
4+
5+
type APIFetcher interface {
6+
FactoidBalance(ctx context.Context, addr string) (int64, error)
7+
EntryCreditBalance(ctx context.Context, addr string) (int64, error)
8+
Height(ctx context.Context) (int64, error)
9+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package api
2+
3+
import (
4+
"context"
5+
6+
"github.com/FactomProject/factom"
7+
)
8+
9+
type FactomFetcher struct {
10+
}
11+
12+
func NewFactomFetcher(factomServer, walletServer string) *FactomFetcher {
13+
if factomServer != "" {
14+
factom.SetFactomdServer(factomServer)
15+
}
16+
if walletServer != "" {
17+
factom.SetWalletServer(walletServer)
18+
}
19+
return &FactomFetcher{}
20+
}
21+
22+
func (fa *FactomFetcher) FactoidBalance(_ context.Context, addr string) (int64, error) {
23+
return factom.GetFactoidBalance(addr)
24+
}
25+
func (fa *FactomFetcher) EntryCreditBalance(_ context.Context, addr string) (int64, error) {
26+
return factom.GetECBalance(addr)
27+
}
28+
29+
func (fa *FactomFetcher) Height(_ context.Context) (int64, error) {
30+
heights, err := factom.GetHeights()
31+
if err != nil {
32+
return -1, err
33+
}
34+
return heights.DirectoryBlockHeight, nil
35+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package api
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"net/http"
9+
"time"
10+
)
11+
12+
type FactomProFetcher struct {
13+
// Cache is a cheeky way to speed things up
14+
Cache map[string]*proBalance
15+
}
16+
17+
func NewFactomPro(ctx context.Context) (*FactomProFetcher, error) {
18+
f := &FactomProFetcher{}
19+
err := f.preloadCache(ctx)
20+
if err != nil {
21+
return nil, fmt.Errorf("preload cache: %w", err)
22+
}
23+
return f, nil
24+
}
25+
26+
type proBalance struct {
27+
PubAddr string `json:"pubAddress"`
28+
PubKey string `json:"pubKey"`
29+
Balance int64 `json:"balance"`
30+
Type string `json:"type"`
31+
}
32+
33+
func (fa *FactomProFetcher) FactoidBalance(ctx context.Context, addr string) (int64, error) {
34+
return fa.balance(ctx, addr)
35+
}
36+
37+
func (fa *FactomProFetcher) EntryCreditBalance(ctx context.Context, addr string) (int64, error) {
38+
return fa.balance(ctx, addr)
39+
}
40+
41+
func (fa *FactomProFetcher) preloadCache(ctx context.Context) error {
42+
if fa.Cache == nil {
43+
fa.Cache = make(map[string]*proBalance)
44+
var ret struct {
45+
Result []*proBalance `json:"result"`
46+
}
47+
48+
_, err := jsonRequest(ctx,
49+
"https://explorer.factom.pro/explorer/richlist",
50+
"GET",
51+
nil,
52+
&ret)
53+
if err != nil {
54+
return err
55+
}
56+
57+
for _, addr := range ret.Result {
58+
fa.Cache[addr.PubAddr] = addr
59+
}
60+
}
61+
return nil
62+
}
63+
64+
func (fa *FactomProFetcher) Height(ctx context.Context) (int64, error) {
65+
var ret struct {
66+
Result struct {
67+
Version string `json:"version"`
68+
BlockchainHeight int64 `json:"blockchainHeight"`
69+
APIHeight int64 `json:"apiHeight"`
70+
Name string `json:"name"`
71+
AnchorsHeight struct {
72+
Btc int `json:"btc"`
73+
Eth int `json:"eth"`
74+
} `json:"anchorsHeight"`
75+
} `json:"result"`
76+
}
77+
78+
_, err := jsonRequest(ctx, "https://explorer.factom.pro/explorer", "GET", nil, &ret)
79+
if err != nil {
80+
return -1, err
81+
}
82+
return ret.Result.BlockchainHeight, nil
83+
}
84+
85+
func (fa *FactomProFetcher) balance(ctx context.Context, addr string) (int64, error) {
86+
if v, ok := fa.Cache[addr]; ok {
87+
return v.Balance, nil
88+
}
89+
90+
var ret struct {
91+
Result proBalance `json:"result"`
92+
}
93+
_, err := jsonRequest(ctx,
94+
fmt.Sprintf("https://explorer.factom.pro/explorer/addresses/%s", addr),
95+
"GET",
96+
nil,
97+
&ret)
98+
if err != nil {
99+
return -1, err
100+
}
101+
return ret.Result.Balance, nil
102+
}
103+
104+
func jsonRequest(ctx context.Context, u string, method string, body interface{}, ret interface{}) (*http.Response, error) {
105+
bodyBuf := bytes.NewBuffer([]byte{})
106+
if body != nil {
107+
err := json.NewEncoder(bodyBuf).Encode(body)
108+
if err != nil {
109+
return nil, fmt.Errorf("encode body: %w", err)
110+
}
111+
}
112+
113+
req, err := http.NewRequestWithContext(ctx, method, u, bodyBuf)
114+
if err != nil {
115+
return nil, fmt.Errorf("new request: %w", err)
116+
}
117+
118+
cli := http.Client{
119+
Timeout: 5 * time.Second,
120+
}
121+
resp, err := cli.Do(req)
122+
if err != nil {
123+
return resp, err
124+
}
125+
defer resp.Body.Close()
126+
127+
if ret != nil {
128+
err := json.NewDecoder(resp.Body).Decode(&ret)
129+
if err != nil {
130+
return resp, fmt.Errorf("decode resp: %w", err)
131+
}
132+
}
133+
134+
return resp, nil
135+
}

0 commit comments

Comments
 (0)