mirror of
https://github.com/alexgo-io/gaze-indexer.git
synced 2026-04-23 19:30:41 +08:00
Compare commits
7 Commits
v0.1.0.bet
...
feature/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2da298ba | ||
|
|
bd9acb8841 | ||
|
|
58761b1ef5 | ||
|
|
85fdf79a00 | ||
|
|
e0e2934160 | ||
|
|
d56c58334a | ||
|
|
be316442ea |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,6 +3,8 @@
|
||||
# Eg. ignore.foo_test.go, ignore.credentials.json, ignore.config.yml
|
||||
ignore.*
|
||||
|
||||
/key
|
||||
|
||||
**/cmd.local/**
|
||||
**/cmd.local.**/**
|
||||
|
||||
|
||||
@@ -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
100
cmd/cmd_generate_keypair.go
Normal 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
|
||||
}
|
||||
@@ -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
8
go.mod
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package httphandler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"slices"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/common/errs"
|
||||
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
|
||||
"github.com/gaze-network/indexer-network/modules/runes/runes"
|
||||
"github.com/gaze-network/uint128"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@@ -83,7 +84,6 @@ type amountWithDecimal struct {
|
||||
type transaction struct {
|
||||
TxHash chainhash.Hash `json:"txHash"`
|
||||
BlockHeight uint64 `json:"blockHeight"`
|
||||
Index uint32 `json:"index"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Inputs []txInputOutput `json:"inputs"`
|
||||
Outputs []txInputOutput `json:"outputs"`
|
||||
@@ -116,6 +116,15 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
blockHeight := req.BlockHeight
|
||||
if blockHeight == 0 {
|
||||
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error during GetLatestBlock")
|
||||
}
|
||||
blockHeight = uint64(blockHeader.Height)
|
||||
}
|
||||
|
||||
var runeId runes.RuneId
|
||||
if req.Id != "" {
|
||||
var ok bool
|
||||
@@ -125,23 +134,68 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
blockHeight := req.BlockHeight
|
||||
// set blockHeight to the latest block height blockHeight, pkScript, and runeId are not provided
|
||||
if blockHeight == 0 && pkScript == nil && runeId == (runes.RuneId{}) {
|
||||
blockHeader, err := h.usecase.GetLatestBlock(ctx.UserContext())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error during GetLatestBlock")
|
||||
}
|
||||
blockHeight = uint64(blockHeader.Height)
|
||||
}
|
||||
|
||||
txs, err := h.usecase.GetRuneTransactions(ctx.UserContext(), pkScript, runeId, blockHeight)
|
||||
txs, err := h.usecase.GetTransactionsByHeight(ctx.UserContext(), blockHeight)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error during GetRuneTransactions")
|
||||
return errors.Wrap(err, "error during GetTransactionsByHeight")
|
||||
}
|
||||
|
||||
var allRuneIds []runes.RuneId
|
||||
filteredTxs := make([]*entity.RuneTransaction, 0)
|
||||
isTxContainPkScript := func(tx *entity.RuneTransaction) bool {
|
||||
for _, input := range tx.Inputs {
|
||||
if bytes.Equal(input.PkScript, pkScript) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, output := range tx.Outputs {
|
||||
if bytes.Equal(output.PkScript, pkScript) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
isTxContainRuneId := func(tx *entity.RuneTransaction) bool {
|
||||
for _, input := range tx.Inputs {
|
||||
if input.RuneId == runeId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, output := range tx.Outputs {
|
||||
if output.RuneId == runeId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for mintedRuneId := range tx.Mints {
|
||||
if mintedRuneId == runeId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for burnedRuneId := range tx.Burns {
|
||||
if burnedRuneId == runeId {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if tx.Runestone != nil {
|
||||
if tx.Runestone.Mint != nil && *tx.Runestone.Mint == runeId {
|
||||
return true
|
||||
}
|
||||
// returns true if this tx etched this runeId
|
||||
if tx.RuneEtched && tx.BlockHeight == runeId.BlockHeight && tx.Index == runeId.TxIndex {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, tx := range txs {
|
||||
if pkScript != nil && !isTxContainPkScript(tx) {
|
||||
continue
|
||||
}
|
||||
if runeId != (runes.RuneId{}) && !isTxContainRuneId(tx) {
|
||||
continue
|
||||
}
|
||||
filteredTxs = append(filteredTxs, tx)
|
||||
}
|
||||
var allRuneIds []runes.RuneId
|
||||
for _, tx := range filteredTxs {
|
||||
for id := range tx.Mints {
|
||||
allRuneIds = append(allRuneIds, id)
|
||||
}
|
||||
@@ -161,12 +215,11 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
|
||||
return errors.Wrap(err, "error during GetRuneEntryByRuneIdBatch")
|
||||
}
|
||||
|
||||
txList := make([]transaction, 0, len(txs))
|
||||
for _, tx := range txs {
|
||||
txList := make([]transaction, 0, len(filteredTxs))
|
||||
for _, tx := range filteredTxs {
|
||||
respTx := transaction{
|
||||
TxHash: tx.Hash,
|
||||
BlockHeight: tx.BlockHeight,
|
||||
Index: tx.Index,
|
||||
Timestamp: tx.Timestamp.Unix(),
|
||||
Inputs: make([]txInputOutput, 0, len(tx.Inputs)),
|
||||
Outputs: make([]txInputOutput, 0, len(tx.Outputs)),
|
||||
@@ -256,13 +309,6 @@ func (h *HttpHandler) GetTransactions(ctx *fiber.Ctx) (err error) {
|
||||
}
|
||||
txList = append(txList, respTx)
|
||||
}
|
||||
// sort by block height ASC, then index ASC
|
||||
slices.SortFunc(txList, func(t1, t2 transaction) int {
|
||||
if t1.BlockHeight != t2.BlockHeight {
|
||||
return int(t1.BlockHeight - t2.BlockHeight)
|
||||
}
|
||||
return int(t1.Index - t2.Index)
|
||||
})
|
||||
|
||||
resp := getTransactionsResponse{
|
||||
Result: &getTransactionsResult{
|
||||
|
||||
@@ -72,7 +72,6 @@ CREATE TABLE IF NOT EXISTS "runes_transactions" (
|
||||
"rune_etched" BOOLEAN NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS runes_transactions_block_height_idx ON "runes_transactions" USING BTREE ("block_height");
|
||||
CREATE INDEX IF NOT EXISTS runes_transactions_jsonb_idx ON "runes_transactions" USING GIN ("inputs", "outputs", "mints", "burns");
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "runes_runestones" (
|
||||
"tx_hash" TEXT NOT NULL PRIMARY KEY,
|
||||
|
||||
@@ -40,23 +40,10 @@ SELECT * FROM runes_entries
|
||||
-- name: GetRuneIdFromRune :one
|
||||
SELECT rune_id FROM runes_entries WHERE rune = $1;
|
||||
|
||||
-- name: GetRuneTransactions :many
|
||||
SELECT * FROM runes_transactions
|
||||
LEFT JOIN runes_runestones ON runes_transactions.hash = runes_runestones.tx_hash
|
||||
WHERE (
|
||||
@filter_pk_script::BOOLEAN = FALSE -- if @filter_pk_script is TRUE, apply pk_script filter
|
||||
OR runes_transactions.outputs @> @pk_script_param::JSONB
|
||||
OR runes_transactions.inputs @> @pk_script_param::JSONB
|
||||
) AND (
|
||||
@filter_rune_id::BOOLEAN = FALSE -- if @filter_rune_id is TRUE, apply rune_id filter
|
||||
OR runes_transactions.outputs @> @rune_id_param::JSONB
|
||||
OR runes_transactions.inputs @> @rune_id_param::JSONB
|
||||
OR runes_transactions.mints ? @rune_id
|
||||
OR runes_transactions.burns ? @rune_id
|
||||
OR (runes_transactions.rune_etched = TRUE AND runes_transactions.block_height = @rune_id_block_height AND runes_transactions.index = @rune_id_tx_index)
|
||||
) AND (
|
||||
@block_height::INT = 0 OR runes_transactions.block_height = @block_height::INT -- if @block_height > 0, apply block_height filter
|
||||
);
|
||||
-- name: GetRuneTransactionsByHeight :many
|
||||
SELECT * FROM runes_transactions
|
||||
LEFT JOIN runes_runestones ON runes_transactions.hash = runes_runestones.tx_hash
|
||||
WHERE runes_transactions.block_height = $1;
|
||||
|
||||
-- name: CountRuneEntries :one
|
||||
SELECT COUNT(*) FROM runes_entries;
|
||||
|
||||
@@ -26,8 +26,7 @@ type RunesDataGatewayWithTx interface {
|
||||
type RunesReaderDataGateway interface {
|
||||
GetLatestBlock(ctx context.Context) (types.BlockHeader, error)
|
||||
GetIndexedBlockByHeight(ctx context.Context, height int64) (*entity.IndexedBlock, error)
|
||||
// GetRuneTransactions returns the runes transactions, filterable by pkScript, runeId and height. If pkScript, runeId or height is zero value, that filter is ignored.
|
||||
GetRuneTransactions(ctx context.Context, pkScript []byte, runeId runes.RuneId, height uint64) ([]*entity.RuneTransaction, error)
|
||||
GetRuneTransactionsByHeight(ctx context.Context, height uint64) ([]*entity.RuneTransaction, error)
|
||||
|
||||
GetRunesBalancesAtOutPoint(ctx context.Context, outPoint wire.OutPoint) (map[runes.RuneId]*entity.OutPointBalance, error)
|
||||
GetUnspentOutPointBalancesByPkScript(ctx context.Context, pkScript []byte, blockHeight uint64) ([]*entity.OutPointBalance, error)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -631,37 +631,13 @@ func (q *Queries) GetRuneIdFromRune(ctx context.Context, rune string) (string, e
|
||||
return rune_id, err
|
||||
}
|
||||
|
||||
const getRuneTransactions = `-- name: GetRuneTransactions :many
|
||||
SELECT hash, runes_transactions.block_height, index, timestamp, inputs, outputs, mints, burns, rune_etched, tx_hash, runes_runestones.block_height, etching, etching_divisibility, etching_premine, etching_rune, etching_spacers, etching_symbol, etching_terms, etching_terms_amount, etching_terms_cap, etching_terms_height_start, etching_terms_height_end, etching_terms_offset_start, etching_terms_offset_end, etching_turbo, edicts, mint, pointer, cenotaph, flaws FROM runes_transactions
|
||||
LEFT JOIN runes_runestones ON runes_transactions.hash = runes_runestones.tx_hash
|
||||
WHERE (
|
||||
$1::BOOLEAN = FALSE -- if @filter_pk_script is TRUE, apply pk_script filter
|
||||
OR runes_transactions.outputs @> $2::JSONB
|
||||
OR runes_transactions.inputs @> $2::JSONB
|
||||
) AND (
|
||||
$3::BOOLEAN = FALSE -- if @filter_rune_id is TRUE, apply rune_id filter
|
||||
OR runes_transactions.outputs @> $4::JSONB
|
||||
OR runes_transactions.inputs @> $4::JSONB
|
||||
OR runes_transactions.mints ? $5
|
||||
OR runes_transactions.burns ? $5
|
||||
OR (runes_transactions.rune_etched = TRUE AND runes_transactions.block_height = $6 AND runes_transactions.index = $7)
|
||||
) AND (
|
||||
$8::INT = 0 OR runes_transactions.block_height = $8::INT -- if @block_height > 0, apply block_height filter
|
||||
)
|
||||
const getRuneTransactionsByHeight = `-- name: GetRuneTransactionsByHeight :many
|
||||
SELECT hash, runes_transactions.block_height, index, timestamp, inputs, outputs, mints, burns, rune_etched, tx_hash, runes_runestones.block_height, etching, etching_divisibility, etching_premine, etching_rune, etching_spacers, etching_symbol, etching_terms, etching_terms_amount, etching_terms_cap, etching_terms_height_start, etching_terms_height_end, etching_terms_offset_start, etching_terms_offset_end, etching_turbo, edicts, mint, pointer, cenotaph, flaws FROM runes_transactions
|
||||
LEFT JOIN runes_runestones ON runes_transactions.hash = runes_runestones.tx_hash
|
||||
WHERE runes_transactions.block_height = $1
|
||||
`
|
||||
|
||||
type GetRuneTransactionsParams struct {
|
||||
FilterPkScript bool
|
||||
PkScriptParam []byte
|
||||
FilterRuneID bool
|
||||
RuneIDParam []byte
|
||||
RuneID []byte
|
||||
RuneIDBlockHeight int32
|
||||
RuneIDTxIndex int32
|
||||
BlockHeight int32
|
||||
}
|
||||
|
||||
type GetRuneTransactionsRow struct {
|
||||
type GetRuneTransactionsByHeightRow struct {
|
||||
Hash string
|
||||
BlockHeight int32
|
||||
Index int32
|
||||
@@ -694,24 +670,15 @@ type GetRuneTransactionsRow struct {
|
||||
Flaws pgtype.Int4
|
||||
}
|
||||
|
||||
func (q *Queries) GetRuneTransactions(ctx context.Context, arg GetRuneTransactionsParams) ([]GetRuneTransactionsRow, error) {
|
||||
rows, err := q.db.Query(ctx, getRuneTransactions,
|
||||
arg.FilterPkScript,
|
||||
arg.PkScriptParam,
|
||||
arg.FilterRuneID,
|
||||
arg.RuneIDParam,
|
||||
arg.RuneID,
|
||||
arg.RuneIDBlockHeight,
|
||||
arg.RuneIDTxIndex,
|
||||
arg.BlockHeight,
|
||||
)
|
||||
func (q *Queries) GetRuneTransactionsByHeight(ctx context.Context, blockHeight int32) ([]GetRuneTransactionsByHeightRow, error) {
|
||||
rows, err := q.db.Query(ctx, getRuneTransactionsByHeight, blockHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetRuneTransactionsRow
|
||||
var items []GetRuneTransactionsByHeightRow
|
||||
for rows.Next() {
|
||||
var i GetRuneTransactionsRow
|
||||
var i GetRuneTransactionsByHeightRow
|
||||
if err := rows.Scan(
|
||||
&i.Hash,
|
||||
&i.BlockHeight,
|
||||
|
||||
@@ -306,7 +306,7 @@ func mapRuneTransactionTypeToParams(src entity.RuneTransaction) (gen.CreateRuneT
|
||||
}, runestoneParams, nil
|
||||
}
|
||||
|
||||
func extractModelRuneTxAndRunestone(src gen.GetRuneTransactionsRow) (gen.RunesTransaction, *gen.RunesRunestone, error) {
|
||||
func extractModelRuneTxAndRunestone(src gen.GetRuneTransactionsByHeightRow) (gen.RunesTransaction, *gen.RunesRunestone, error) {
|
||||
var runestone *gen.RunesRunestone
|
||||
if src.TxHash.Valid {
|
||||
// these fields should never be null
|
||||
|
||||
@@ -3,7 +3,6 @@ package postgres
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
@@ -62,21 +61,8 @@ func (r *Repository) GetIndexedBlockByHeight(ctx context.Context, height int64)
|
||||
return indexedBlock, nil
|
||||
}
|
||||
|
||||
func (r *Repository) GetRuneTransactions(ctx context.Context, pkScript []byte, runeId runes.RuneId, height uint64) ([]*entity.RuneTransaction, error) {
|
||||
pkScriptParam := []byte(fmt.Sprintf(`[{"pkScript":"%s"}]`, hex.EncodeToString(pkScript)))
|
||||
runeIdParam := []byte(fmt.Sprintf(`[{"runeId":"%s"}]`, runeId.String()))
|
||||
rows, err := r.queries.GetRuneTransactions(ctx, gen.GetRuneTransactionsParams{
|
||||
FilterPkScript: pkScript != nil,
|
||||
PkScriptParam: pkScriptParam,
|
||||
|
||||
FilterRuneID: runeId != runes.RuneId{},
|
||||
RuneIDParam: runeIdParam,
|
||||
RuneID: []byte(runeId.String()),
|
||||
RuneIDBlockHeight: int32(runeId.BlockHeight),
|
||||
RuneIDTxIndex: int32(runeId.TxIndex),
|
||||
|
||||
BlockHeight: int32(height),
|
||||
})
|
||||
func (r *Repository) GetRuneTransactionsByHeight(ctx context.Context, height uint64) ([]*entity.RuneTransaction, error) {
|
||||
rows, err := r.queries.GetRuneTransactionsByHeight(ctx, int32(height))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error during query")
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/modules/runes/internal/entity"
|
||||
"github.com/gaze-network/indexer-network/modules/runes/runes"
|
||||
)
|
||||
|
||||
func (u *Usecase) GetRuneTransactions(ctx context.Context, pkScript []byte, runeId runes.RuneId, height uint64) ([]*entity.RuneTransaction, error) {
|
||||
txs, err := u.runesDg.GetRuneTransactions(ctx, pkScript, runeId, height)
|
||||
func (u *Usecase) GetTransactionsByHeight(ctx context.Context, height uint64) ([]*entity.RuneTransaction, error) {
|
||||
txs, err := u.runesDg.GetRuneTransactionsByHeight(ctx, height)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error during GetTransactionsByHeight")
|
||||
}
|
||||
|
||||
105
pkg/crypto/crypto.go
Normal file
105
pkg/crypto/crypto.go
Normal 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
65
pkg/crypto/crypto_test.go
Normal 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)
|
||||
}
|
||||
168
pkg/reportingclientv2/reportingclient.go
Normal file
168
pkg/reportingclientv2/reportingclient.go
Normal 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
|
||||
}
|
||||
96
pkg/reportingclientv2/reportingclient_test.go
Normal file
96
pkg/reportingclientv2/reportingclient_test.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user