Compare commits

..

9 Commits

Author SHA1 Message Date
PanJ
4b2da298ba read private key from file 2024-05-21 15:18:55 +07:00
Panjamapong Sermsawatsri
bd9acb8841 feat: fix reporting errors 2024-05-15 17:09:48 +07:00
PanJ
58761b1ef5 update reporting api 2024-05-15 15:33:59 +07:00
Panjamapong Sermsawatsri
85fdf79a00 fix: wif file name to add mainnet 2024-05-08 10:35:21 +07:00
Panjamapong Sermsawatsri
e0e2934160 feat: add wif generation 2024-05-08 10:28:47 +07:00
Panjamapong Sermsawatsri
d56c58334a feat: add new reporting client 2024-04-26 16:20:24 +07:00
Panjamapong Sermsawatsri
be316442ea feat: generate key command and crypto pkg 2024-04-23 16:44:16 +07:00
Gaze
185d26c651 feat: add more index 2024-04-23 13:28:29 +07:00
Gaze
a0b15f452d fix: wrong condition 2024-04-23 00:49:19 +07:00
15 changed files with 1636 additions and 22 deletions

2
.gitignore vendored
View File

@@ -3,6 +3,8 @@
# Eg. ignore.foo_test.go, ignore.credentials.json, ignore.config.yml
ignore.*
/key
**/cmd.local/**
**/cmd.local.**/**

View File

@@ -14,13 +14,14 @@ var (
// root command
cmd = &cobra.Command{
Use: "gaze",
Long: `Description of gaze indexer`,
Long: `Gaze in a Bitcoin meta-protocol indexer`,
}
// sub-commands
// sub-commandsf
cmds = []*cobra.Command{
NewVersionCommand(),
NewRunCommand(),
NewGenerateKeypairCommand(),
}
)

100
cmd/cmd_generate_keypair.go Normal file
View File

@@ -0,0 +1,100 @@
package cmd
import (
"crypto/rand"
"encoding/hex"
"fmt"
"os"
"path"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common/errs"
"github.com/gaze-network/indexer-network/pkg/crypto"
"github.com/spf13/cobra"
)
type generateKeypairCmdOptions struct {
Path string
}
func NewGenerateKeypairCommand() *cobra.Command {
opts := &generateKeypairCmdOptions{}
cmd := &cobra.Command{
Use: "generate-keypair",
Short: "Generate new public/private keypair for encryption and signature generation",
RunE: func(cmd *cobra.Command, args []string) error {
return generateKeypairHandler(opts, cmd, args)
},
}
flags := cmd.Flags()
flags.StringVar(&opts.Path, "path", "/data/keys", `Path to save to key pair file`)
return cmd
}
func generateKeypairHandler(opts *generateKeypairCmdOptions, _ *cobra.Command, _ []string) error {
fmt.Printf("Generating key pair\n")
privKeyBytes := make([]byte, 32)
_, err := rand.Read(privKeyBytes)
if err != nil {
return errors.Wrap(errs.SomethingWentWrong, "random bytes")
}
_, pubKey := btcec.PrivKeyFromBytes(privKeyBytes)
serializedPubKey := pubKey.SerializeCompressed()
// fmt.Println(hex.EncodeToString(privKeyBytes))
fmt.Printf("Public key: %s\n", hex.EncodeToString(serializedPubKey))
err = os.MkdirAll(opts.Path, 0o755)
if err != nil {
return errors.Wrap(errs.SomethingWentWrong, "create directory")
}
privateKeyPath := path.Join(opts.Path, "priv.key")
_, err = os.Stat(privateKeyPath)
if err == nil {
fmt.Printf("Existing private key found at %s\n[WARNING] THE EXISTING PRIVATE KEY WILL BE LOST\nType [replace] to replace existing private key: ", privateKeyPath)
var ans string
fmt.Scanln(&ans)
if ans != "replace" {
fmt.Printf("Keypair generation aborted\n")
return nil
}
}
err = os.WriteFile(privateKeyPath, []byte(hex.EncodeToString(privKeyBytes)), 0o644)
if err != nil {
return errors.Wrap(err, "write private key file")
}
fmt.Printf("Private key saved at %s\n", privateKeyPath)
wifKeyPath := path.Join(opts.Path, "priv_wif_mainnet.key")
client, err := crypto.New(hex.EncodeToString(privKeyBytes))
if err != nil {
return errors.Wrap(err, "new crypto client")
}
wifKey, err := client.WIF(&chaincfg.MainNetParams)
if err != nil {
return errors.Wrap(err, "get WIF key")
}
err = os.WriteFile(wifKeyPath, []byte(wifKey), 0o644)
if err != nil {
return errors.Wrap(err, "write WIF private key file")
}
fmt.Printf("WIF private key saved at %s\n", wifKeyPath)
publicKeyPath := path.Join(opts.Path, "pub.key")
err = os.WriteFile(publicKeyPath, []byte(hex.EncodeToString(serializedPubKey)), 0o644)
if err != nil {
return errors.Wrap(errs.SomethingWentWrong, "write public key file")
}
fmt.Printf("Public key saved at %s\n", publicKeyPath)
return nil
}

View File

@@ -30,7 +30,7 @@ import (
"github.com/gaze-network/indexer-network/pkg/errorhandler"
"github.com/gaze-network/indexer-network/pkg/logger"
"github.com/gaze-network/indexer-network/pkg/logger/slogx"
"github.com/gaze-network/indexer-network/pkg/reportingclient"
"github.com/gaze-network/indexer-network/pkg/reportingclientv2"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
fiberrecover "github.com/gofiber/fiber/v2/middleware/recover"
@@ -90,6 +90,16 @@ func runHandler(opts *runCmdOptions, cmd *cobra.Command, _ []string) error {
// Add logger context
ctx = logger.WithContext(ctx, slogx.Stringer("network", conf.Network))
// Load private key
privKeyPath := conf.NodeKey.Path
if privKeyPath == "" {
privKeyPath = "/data/keys/priv.key"
}
privKeyByte, err := os.ReadFile(privKeyPath)
if err != nil {
logger.PanicContext(ctx, "Failed to read private key file", slogx.Error(err))
}
// Initialize Bitcoin Core RPC Client
client, err := rpcclient.New(&rpcclient.ConnConfig{
Host: conf.BitcoinNode.Host,
@@ -123,9 +133,9 @@ func runHandler(opts *runCmdOptions, cmd *cobra.Command, _ []string) error {
// use gracefulEG to coordinate graceful shutdown after context is done. (e.g. shut down http server, shutdown logic of each module, etc.)
gracefulEG, gctx := errgroup.WithContext(context.Background())
var reportingClient *reportingclient.ReportingClient
var reportingClient *reportingclientv2.ReportingClient
if !conf.Reporting.Disabled {
reportingClient, err = reportingclient.New(conf.Reporting)
reportingClient, err = reportingclientv2.New(conf.Reporting, string(privKeyByte)) // TODO: read private key from file
if err != nil {
logger.PanicContext(ctx, "Failed to create reporting client", slogx.Error(err))
}

8
go.mod
View File

@@ -26,15 +26,17 @@ require (
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/ecies/go/v2 v2.0.9 // indirect
github.com/ethereum/go-ethereum v1.13.5 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/getsentry/sentry-go v0.18.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect

1058
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@ import (
runesconfig "github.com/gaze-network/indexer-network/modules/runes/config"
"github.com/gaze-network/indexer-network/pkg/logger"
"github.com/gaze-network/indexer-network/pkg/logger/slogx"
"github.com/gaze-network/indexer-network/pkg/reportingclient"
"github.com/gaze-network/indexer-network/pkg/reportingclientv2"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
@@ -32,12 +32,17 @@ var (
)
type Config struct {
Logger logger.Config `mapstructure:"logger"`
BitcoinNode BitcoinNodeClient `mapstructure:"bitcoin_node"`
Network common.Network `mapstructure:"network"`
HTTPServer HTTPServerConfig `mapstructure:"http_server"`
Modules Modules `mapstructure:"modules"`
Reporting reportingclient.Config `mapstructure:"reporting"`
Logger logger.Config `mapstructure:"logger"`
BitcoinNode BitcoinNodeClient `mapstructure:"bitcoin_node"`
Network common.Network `mapstructure:"network"`
HTTPServer HTTPServerConfig `mapstructure:"http_server"`
Modules Modules `mapstructure:"modules"`
Reporting reportingclientv2.Config `mapstructure:"reporting"`
NodeKey NodeKey `mapstructure:"node_key"`
}
type NodeKey struct {
Path string `mapstructure:"path"`
}
type BitcoinNodeClient struct {

View File

@@ -189,7 +189,7 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
if pkScript != nil && !isTxContainPkScript(tx) {
continue
}
if runeId != (runes.RuneId{}) && isTxContainRuneId(tx) {
if runeId != (runes.RuneId{}) && !isTxContainRuneId(tx) {
continue
}
filteredTxs = append(filteredTxs, tx)

View File

@@ -71,6 +71,7 @@ CREATE TABLE IF NOT EXISTS "runes_transactions" (
"burns" JSONB NOT NULL,
"rune_etched" BOOLEAN NOT NULL
);
CREATE INDEX IF NOT EXISTS runes_transactions_block_height_idx ON "runes_transactions" USING BTREE ("block_height");
CREATE TABLE IF NOT EXISTS "runes_runestones" (
"tx_hash" TEXT NOT NULL PRIMARY KEY,
@@ -107,6 +108,7 @@ CREATE TABLE IF NOT EXISTS "runes_outpoint_balances" (
PRIMARY KEY ("rune_id", "tx_hash", "tx_idx")
);
CREATE INDEX IF NOT EXISTS runes_outpoint_balances_tx_hash_tx_idx_idx ON "runes_outpoint_balances" USING BTREE ("tx_hash", "tx_idx");
CREATE INDEX IF NOT EXISTS runes_outpoint_balances_pkscript_block_height_spent_height_idx ON "runes_outpoint_balances" USING BTREE ("pkscript", "block_height", "spent_height");
CREATE TABLE IF NOT EXISTS "runes_balances" (
"pkscript" TEXT NOT NULL,

View File

@@ -16,7 +16,7 @@ import (
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
"github.com/gaze-network/indexer-network/modules/runes/runes"
"github.com/gaze-network/indexer-network/pkg/logger"
"github.com/gaze-network/indexer-network/pkg/reportingclient"
"github.com/gaze-network/indexer-network/pkg/reportingclientv2"
"github.com/gaze-network/uint128"
"github.com/samber/lo"
)
@@ -29,7 +29,7 @@ type Processor struct {
bitcoinClient btcclient.Contract
bitcoinDataSource indexers.BitcoinDatasource
network common.Network
reportingClient *reportingclient.ReportingClient
reportingClient *reportingclientv2.ReportingClient
newRuneEntries map[runes.RuneId]*runes.RuneEntry
newRuneEntryStates map[runes.RuneId]*runes.RuneEntry
@@ -39,7 +39,7 @@ type Processor struct {
newRuneTxs []*entity.RuneTransaction
}
func NewProcessor(runesDg datagateway.RunesDataGateway, indexerInfoDg datagateway.IndexerInfoDataGateway, bitcoinClient btcclient.Contract, bitcoinDataSource indexers.BitcoinDatasource, network common.Network, reportingClient *reportingclient.ReportingClient) *Processor {
func NewProcessor(runesDg datagateway.RunesDataGateway, indexerInfoDg datagateway.IndexerInfoDataGateway, bitcoinClient btcclient.Contract, bitcoinDataSource indexers.BitcoinDatasource, network common.Network, reportingClient *reportingclientv2.ReportingClient) *Processor {
return &Processor{
runesDg: runesDg,
indexerInfoDg: indexerInfoDg,
@@ -72,7 +72,7 @@ func (p *Processor) VerifyStates(ctx context.Context) error {
}
}
if p.reportingClient != nil {
if err := p.reportingClient.SubmitNodeReport(ctx, "runes", p.network); err != nil {
if err := p.reportingClient.SubmitNodeReport(ctx, "runes", p.network, Version); err != nil {
return errors.Wrap(err, "failed to submit node report")
}
}

View File

@@ -18,7 +18,7 @@ import (
"github.com/gaze-network/indexer-network/modules/runes/runes"
"github.com/gaze-network/indexer-network/pkg/logger"
"github.com/gaze-network/indexer-network/pkg/logger/slogx"
"github.com/gaze-network/indexer-network/pkg/reportingclient"
"github.com/gaze-network/indexer-network/pkg/reportingclientv2"
"github.com/gaze-network/uint128"
"github.com/samber/lo"
)
@@ -810,7 +810,7 @@ func (p *Processor) flushBlock(ctx context.Context, blockHeader types.BlockHeade
// submit event to reporting system
if p.reportingClient != nil {
if err := p.reportingClient.SubmitBlockReport(ctx, reportingclient.SubmitBlockReportPayload{
if err := p.reportingClient.SubmitBlockReport(ctx, reportingclientv2.SubmitBlockReportPayloadData{
Type: "runes",
ClientVersion: Version,
DBVersion: DBVersion,

105
pkg/crypto/crypto.go Normal file
View File

@@ -0,0 +1,105 @@
package crypto
import (
"encoding/base64"
"encoding/hex"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cockroachdb/errors"
ecies "github.com/ecies/go/v2"
)
type Client struct {
privateKey *btcec.PrivateKey
eciesPrivateKey *ecies.PrivateKey
}
func New(privateKeyStr string) (*Client, error) {
if privateKeyStr != "" {
privateKeyBytes, err := hex.DecodeString(privateKeyStr)
if err != nil {
return nil, errors.Wrap(err, "decode private key")
}
privateKey, _ := btcec.PrivKeyFromBytes(privateKeyBytes)
eciesPrivateKey := ecies.NewPrivateKeyFromBytes(privateKeyBytes)
return &Client{
privateKey: privateKey,
eciesPrivateKey: eciesPrivateKey,
}, nil
}
return &Client{}, nil
}
func (c *Client) PublicKey() string {
return hex.EncodeToString(c.privateKey.PubKey().SerializeCompressed())
}
func (c *Client) WIF(params *chaincfg.Params) (string, error) {
wif, err := btcutil.NewWIF(c.privateKey, params, true)
if err != nil {
return "", errors.Wrap(err, "wif convert failed")
}
return wif.String(), nil
}
func (c *Client) Sign(message string) string {
messageHash := chainhash.DoubleHashB([]byte(message))
signature := ecdsa.Sign(c.privateKey, messageHash)
return hex.EncodeToString(signature.Serialize())
}
func (c *Client) Verify(message, sigStr, pubKeyStr string) (bool, error) {
sigBytes, err := hex.DecodeString(sigStr)
if err != nil {
return false, errors.Wrap(err, "signature decode")
}
pubBytes, err := hex.DecodeString(pubKeyStr)
if err != nil {
return false, errors.Wrap(err, "pubkey decode")
}
pubKey, err := btcec.ParsePubKey(pubBytes)
if err != nil {
return false, errors.Wrap(err, "pubkey parse")
}
messageHash := chainhash.DoubleHashB([]byte(message))
signature, err := ecdsa.ParseSignature(sigBytes)
if err != nil {
return false, errors.Wrap(err, "signature parse")
}
return signature.Verify(messageHash, pubKey), nil
}
func (c *Client) Encrypt(message, pubKeyStr string) (string, error) {
pubKey, err := ecies.NewPublicKeyFromHex(pubKeyStr)
if err != nil {
return "", errors.Wrap(err, "parse pubkey")
}
ciphertext, err := ecies.Encrypt(pubKey, []byte(message))
if err != nil {
return "", errors.Wrap(err, "encrypt message")
}
ciphertextStr := base64.StdEncoding.EncodeToString(ciphertext)
return ciphertextStr, nil
}
func (c *Client) Decrypt(ciphertextStr string) (string, error) {
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextStr)
if err != nil {
return "", errors.Wrap(err, "decode ciphertext")
}
plaintext, err := ecies.Decrypt(c.eciesPrivateKey, ciphertext)
if err != nil {
return "", errors.Wrap(err, "decrypt")
}
return string(plaintext), nil
}

65
pkg/crypto/crypto_test.go Normal file
View File

@@ -0,0 +1,65 @@
package crypto
import (
"testing"
"github.com/btcsuite/btcd/chaincfg"
"github.com/stretchr/testify/assert"
)
// Key for test only. DO NOT USE IN PRODUCTION
const (
privateKeyStr = "ce9c2fd75623e82a83ed743518ec7749f6f355f7301dd432400b087717fed2f2"
mainnetKey = "L49LKamtrPZxty5TG7jaFPHMRZbrvAr4Dvn5BHGdvmvbcTDNAbZj"
pubKeyStr = "0251e2dfcdeea17cc9726e4be0855cd0bae19e64f3e247b10760cd76851e7df47e"
)
func TestEncryptDecrypt(t *testing.T) {
plaintext := "hello world"
privClient, err := New(privateKeyStr)
assert.NoError(t, err)
pubClient, err := New("")
assert.NoError(t, err)
ciphertext, err := pubClient.Encrypt(plaintext, pubKeyStr)
assert.NoError(t, err)
decrypted, err := privClient.Decrypt(ciphertext)
assert.NoError(t, err)
assert.Equal(t, plaintext, decrypted)
}
func TestSignVerify(t *testing.T) {
plaintext := "hello world"
invalidSignature := "3044022066504a82e2bc23167214e05497a1ca957add9cacc078aa69f5417079a4d56f0002206b215920b046c779d4a58d4029c26dbadcaf6d3c884b3463f44e70ef9146c1cd"
privClient, err := New(privateKeyStr)
assert.NoError(t, err)
pubClient, err := New("")
assert.NoError(t, err)
signature := privClient.Sign(plaintext)
println(signature)
verified, err := pubClient.Verify(plaintext, signature, pubKeyStr)
assert.NoError(t, err)
assert.True(t, verified)
verified, err = pubClient.Verify(plaintext, invalidSignature, pubKeyStr)
assert.NoError(t, err)
assert.False(t, verified)
}
func TestWIF(t *testing.T) {
privClient, err := New(privateKeyStr)
assert.NoError(t, err)
wifPrivKey, err := privClient.WIF(&chaincfg.MainNetParams)
assert.NoError(t, err)
assert.Equal(t, wifPrivKey, mainnetKey)
}

View File

@@ -0,0 +1,168 @@
package reportingclientv2
import (
"context"
"encoding/json"
"log/slog"
"github.com/Cleverse/go-utilities/utils"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cockroachdb/errors"
"github.com/gaze-network/indexer-network/common"
"github.com/gaze-network/indexer-network/pkg/crypto"
"github.com/gaze-network/indexer-network/pkg/httpclient"
"github.com/gaze-network/indexer-network/pkg/logger"
)
type Config struct {
Disabled bool `mapstructure:"disabled"`
ReportCenter ReportCenter `mapstructure:"report_center"`
NodeInfo NodeInfo `mapstructure:"node_info"`
}
type NodeInfo struct {
Name string `mapstructure:"name"`
WebsiteURL string `mapstructure:"website_url"`
APIURL string `mapstructure:"api_url"`
}
type ReportCenter struct {
BaseURL string `mapstructure:"base_url"`
PublicKey string `mapstructure:"public_key"`
}
type ReportingClient struct {
httpClient *httpclient.Client
cryptoClient *crypto.Client
config Config
}
const (
defaultBaseURL = "https://indexer.api.gaze.network"
defaultPublicKey = "039298683d53a1cbdb6f318d5ad4b12bc0d752f3a6cd62c19b2c22b1ae1e12fe05"
)
func New(config Config, indexerPrivateKey string) (*ReportingClient, error) {
config.ReportCenter.BaseURL = utils.Default(config.ReportCenter.BaseURL, defaultBaseURL)
config.ReportCenter.PublicKey = utils.Default(config.ReportCenter.PublicKey, defaultPublicKey)
httpClient, err := httpclient.New(config.ReportCenter.BaseURL)
if err != nil {
return nil, errors.Wrap(err, "can't create http client")
}
cryptoClient, err := crypto.New(indexerPrivateKey)
if err != nil {
return nil, errors.Wrap(err, "can't create crypto client")
}
return &ReportingClient{
httpClient: httpClient,
config: config,
cryptoClient: cryptoClient,
}, nil
}
type SubmitBlockReportPayload struct {
EncryptedData string `json:"encryptedData"`
IndexerPublicKey string `json:"indexerPublicKey"`
}
type SubmitBlockReportPayloadData struct {
Type string `json:"type"`
ClientVersion string `json:"clientVersion"`
DBVersion int `json:"dbVersion"`
EventHashVersion int `json:"eventHashVersion"`
Network common.Network `json:"network"`
BlockHeight uint64 `json:"blockHeight"`
BlockHash chainhash.Hash `json:"blockHash"`
EventHash chainhash.Hash `json:"eventHash"`
CumulativeEventHash chainhash.Hash `json:"cumulativeEventHash"`
IndexerPublicKey string `json:"indexerPublicKey"`
}
func (r *ReportingClient) SubmitBlockReport(ctx context.Context, payload SubmitBlockReportPayloadData) error {
payload.IndexerPublicKey = r.cryptoClient.PublicKey()
data, err := json.Marshal(payload)
if err != nil {
return errors.Wrap(err, "can't marshal payload")
}
encryptedData, err := r.cryptoClient.Encrypt(string(data), r.config.ReportCenter.PublicKey)
if err != nil {
return errors.Wrap(err, "can't encrypt data")
}
bodyStruct := SubmitBlockReportPayload{
EncryptedData: encryptedData,
IndexerPublicKey: r.cryptoClient.PublicKey(),
}
body, err := json.Marshal(bodyStruct)
if err != nil {
return errors.Wrap(err, "can't marshal payload")
}
resp, err := r.httpClient.Post(ctx, "/v2/report/block", httpclient.RequestOptions{
Body: body,
})
if err != nil {
return errors.Wrap(err, "can't send request")
}
if resp.StatusCode() >= 400 {
logger.WarnContext(ctx, "failed to submit block report", slog.Any("payload", bodyStruct), slog.Any("responseBody", resp.Body()))
} else {
logger.DebugContext(ctx, "block report submitted", slog.Any("payload", bodyStruct))
}
return nil
}
type SubmitNodeReportPayload struct {
Data SubmitNodeReportPayloadData `json:"data"`
IndexerPublicKey string `json:"indexerPublicKey"`
Signature string `json:"string"`
}
type SubmitNodeReportPayloadData struct {
Name string `json:"name"`
Type string `json:"type"`
Network common.Network `json:"network"`
WebsiteURL string `json:"websiteUrl,omitempty"`
APIURL string `json:"apiUrl,omitempty"`
IndexerPublicKey string `json:"indexerPublicKey"`
ClientVersion string `json:"clientVersion"`
}
func (r *ReportingClient) SubmitNodeReport(ctx context.Context, module string, network common.Network, clientVersion string) error {
payload := SubmitNodeReportPayload{
Data: SubmitNodeReportPayloadData{
Name: r.config.NodeInfo.Name,
Type: module,
Network: network,
WebsiteURL: r.config.NodeInfo.WebsiteURL,
APIURL: r.config.NodeInfo.APIURL,
IndexerPublicKey: r.cryptoClient.PublicKey(),
ClientVersion: clientVersion,
},
IndexerPublicKey: r.cryptoClient.PublicKey(),
}
dataPayload, err := json.Marshal(payload.Data)
if err != nil {
return errors.Wrap(err, "can't marshal payload data")
}
payload.Signature = r.cryptoClient.Sign(string(dataPayload))
body, err := json.Marshal(payload)
if err != nil {
return errors.Wrap(err, "can't marshal payload")
}
resp, err := r.httpClient.Post(ctx, "/v2/report/node", httpclient.RequestOptions{
Body: body,
})
if err != nil {
return errors.Wrap(err, "can't send request")
}
if resp.StatusCode() >= 400 {
logger.WarnContext(ctx, "failed to submit node report", slog.Any("payload", payload), slog.Any("responseBody", resp.Body()))
} else {
logger.InfoContext(ctx, "node report submitted", slog.Any("payload", payload))
}
return nil
}

View File

@@ -0,0 +1,96 @@
package reportingclientv2
import (
"context"
"fmt"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/gaze-network/indexer-network/common"
"github.com/stretchr/testify/assert"
)
// Key for test only. DO NOT USE IN PRODUCTION
const (
privateKeyStrOfficial = "ce9c2fd75623e82a83ed743518ec7749f6f355f7301dd432400b087717fed2f2"
mainnetKeyOfficial = "L2hqSEYLjRQHHSiSgfNSFwaZ1dYpwn4PUTt2nQT8AefzYGQCwsGY"
pubKeyStrOfficial = "0251e2dfcdeea17cc9726e4be0855cd0bae19e64f3e247b10760cd76851e7df47e"
)
const (
privateKeyStr1 = "a3a7d2c40c8bb7a4b4afa9a6b3ed613da1de233913ed07a017e6dd44ef542d80"
mainnetKey1 = "L49LKamtrPZxty5TG7jaFPHMRZbrvAr4Dvn5BHGdvmvbcTDNAbZj"
pubKeyStr1 = "02596e11b5a2104533d2732a3df35eadaeb61b189b9069715106e72e27c1de7775"
)
const (
privateKeyStr2 = "a3a7d2c40c8bb7a4b4afa9a6b3ed613da1de233913ed07a017e6dd44ef542d81"
// mainnetKey1 = "L49LKamtrPZxty5TG7jaFPHMRZbrvAr4Dvn5BHGdvmvbcTDNAbZj"
// pubKeyStr1 = "02596e11b5a2104533d2732a3df35eadaeb61b189b9069715106e72e27c1de7775"
)
func TestReport1(t *testing.T) {
block := 18
hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%d", block))
assert.NoError(t, err)
configOfficial := Config{
ReportCenter: ReportCenter{
BaseURL: "https://indexer-dev.api.gaze.network",
PublicKey: defaultPublicKey,
},
NodeInfo: NodeInfo{
Name: "Official Node",
APIURL: "http://localhost:2000",
},
}
config1 := Config{
ReportCenter: ReportCenter{
BaseURL: "https://indexer-dev.api.gaze.network",
PublicKey: defaultPublicKey,
},
NodeInfo: NodeInfo{
Name: "Node 1",
APIURL: "http://localhost:2000",
},
}
config2 := Config{
ReportCenter: ReportCenter{
BaseURL: "https://indexer-dev.api.gaze.network",
PublicKey: defaultPublicKey,
},
NodeInfo: NodeInfo{
Name: "Node 2",
APIURL: "http://localhost:2000",
},
}
clientOfficial, err := New(configOfficial, privateKeyStrOfficial)
assert.NoError(t, err)
client1, err := New(config1, privateKeyStr1)
assert.NoError(t, err)
client2, err := New(config2, privateKeyStr2)
assert.NoError(t, err)
err = clientOfficial.SubmitNodeReport(context.Background(), "runes", "mainnet", "v0.0.1")
assert.NoError(t, err)
err = client1.SubmitNodeReport(context.Background(), "runes", "mainnet", "v0.0.1")
assert.NoError(t, err)
err = client2.SubmitNodeReport(context.Background(), "runes", "mainnet", "v0.0.1")
assert.NoError(t, err)
blockReport := SubmitBlockReportPayloadData{
Type: "runes",
ClientVersion: "v0.0.1",
DBVersion: 1,
EventHashVersion: 1,
Network: common.NetworkMainnet,
BlockHeight: uint64(block),
BlockHash: *hash,
EventHash: *hash,
CumulativeEventHash: *hash,
}
err = clientOfficial.SubmitBlockReport(context.Background(), blockReport)
err = client1.SubmitBlockReport(context.Background(), blockReport)
err = client2.SubmitBlockReport(context.Background(), blockReport)
}