feat: mostly implemented nodesale protocol parser.

This commit is contained in:
Waris Aiemworawutikul
2024-06-04 14:57:08 +07:00
parent 80db77de6a
commit d563ddbed2
16 changed files with 663 additions and 233 deletions

View File

@@ -31,6 +31,7 @@ CREATE TABLE IF NOT EXISTS node_sales (
"seller_public_key" TEXT NOT NULL,
"max_per_address" INTEGER NOT NULL,
"deploy_tx_hash" TEXT NOT NULL REFERENCES events(tx_hash) ON DELETE CASCADE,
"max_discount_percentage" INTEGER NOT NULL,
PRIMARY KEY ("block_height", "tx_index")
);
@@ -39,10 +40,10 @@ CREATE TABLE IF NOT EXISTS nodes (
"sale_tx_index" INTEGER NOT NULL,
"node_id" INTEGER NOT NULL,
"tier_index" INTEGER NOT NULL,
"delegated_to" TEXT NOT NULL,
"delegated_to" TEXT,
"owner_public_key" TEXT NOT NULL,
"purchase_tx_hash" TEXT NOT NULL REFERENCES events(tx_hash) ON DELETE CASCADE,
"delegate_tx_hash" TEXT NOT NULL REFERENCES events(tx_hash) ON DELETE SET NULL,
"delegate_tx_hash" TEXT REFERENCES events(tx_hash) ON DELETE SET NULL,
PRIMARY KEY("sale_block", "sale_tx_index", "node_id"),
FOREIGN KEY("sale_block", "sale_tx_index") REFERENCES node_sales("block_height", "tx_index")
);

View File

@@ -4,5 +4,5 @@ WHERE "block_height" >= @from_block;
-- name: AddEvent :exec
INSERT INTO events("tx_hash", "block_height", "tx_index", "wallet_address", "valid", "action",
"raw_message", "parsed_message", "block_timestamp", "block_hash")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
"raw_message", "parsed_message", "block_timestamp", "block_hash", "metadata")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);

View File

@@ -15,4 +15,17 @@ SELECT *
FROM nodes
WHERE sale_block = $1 AND
sale_tx_index = $2 AND
node_id = ANY (@node_ids::int[]);
node_id = ANY (@node_ids::int[]);
-- name: GetNodesByOwner :many
SELECT *
FROM nodes
WHERE sale_block = $1 AND
sale_tx_index = $2 AND
owner_public_key = $3
ORDER BY tier_index;
-- name: AddNode :exec
INSERT INTO nodes(sale_block, sale_tx_index, node_id, tier_index, delegated_to, owner_public_key, purchase_tx_hash, delegate_tx_hash)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8);

View File

@@ -1,3 +1,9 @@
-- name: AddNodesale :exec
INSERT INTO node_sales("block_height", "tx_index", "name", "starts_at", "ends_at", "tiers", "seller_public_key", "max_per_address", "deploy_tx_hash")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9);
INSERT INTO node_sales("block_height", "tx_index", "name", "starts_at", "ends_at", "tiers", "seller_public_key", "max_per_address", "deploy_tx_hash", "max_discount_percentage")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10);
-- name: GetNodesale :many
SELECT *
FROM node_sales
WHERE block_height = $1 AND
tx_index = $2;

View File

@@ -0,0 +1,77 @@
package nodesale
import (
"bytes"
"context"
"fmt"
"github.com/gaze-network/indexer-network/core/types"
"github.com/gaze-network/indexer-network/modules/nodesale/repository/postgres/gen"
"github.com/jackc/pgx/v5/pgtype"
)
func (p *Processor) processDelegate(ctx context.Context, qtx gen.Querier, block *types.Block, event nodesaleEvent) error {
valid := true
delegate := event.eventMessage.Delegate
nodeIds := make([]int32, len(delegate.NodeIDs))
for _, id := range delegate.NodeIDs {
nodeIds = append(nodeIds, int32(id))
}
nodes, err := qtx.GetNodes(ctx, gen.GetNodesParams{
SaleBlock: int32(delegate.DeployID.Block),
SaleTxIndex: int32(delegate.DeployID.TxIndex),
NodeIds: nodeIds,
})
if err != nil {
return fmt.Errorf("Failed to get nodes : %w", err)
}
if len(nodeIds) != len(nodes) {
valid = false
}
if valid {
for _, node := range nodes {
ownerAddress := p.pubkeyToAddress(node.OwnerPublicKey)
if !bytes.Equal(
[]byte(ownerAddress.EncodeAddress()),
[]byte(event.txAddress.EncodeAddress()),
) {
valid = false
}
}
}
err = qtx.AddEvent(ctx, gen.AddEventParams{
TxHash: event.transaction.TxHash.String(),
TxIndex: int32(event.transaction.Index),
Action: int32(event.eventMessage.Action),
RawMessage: event.rawData,
ParsedMessage: event.eventJson,
BlockTimestamp: pgtype.Timestamp{Time: block.Header.Timestamp, Valid: true},
BlockHash: block.Header.Hash.String(),
BlockHeight: int32(block.Header.Height),
Valid: valid,
WalletAddress: event.txAddress.EncodeAddress(),
Metadata: []byte{},
})
if err != nil {
return fmt.Errorf("Failed to insert event : %w", err)
}
if valid {
_, err = qtx.SetDelegates(ctx, gen.SetDelegatesParams{
SaleBlock: int32(event.transaction.BlockHeight),
SaleTxIndex: int32(event.transaction.Index),
Delegatee: pgtype.Text{
String: string(delegate.DelegateePublicKey),
Valid: true,
},
NodeIds: nodeIds,
})
if err != nil {
return fmt.Errorf("Failed to set delegate : %w", err)
}
}
return nil
}

View File

@@ -0,0 +1,75 @@
package nodesale
import (
"bytes"
"context"
"fmt"
"time"
"github.com/gaze-network/indexer-network/core/types"
"github.com/gaze-network/indexer-network/modules/nodesale/repository/postgres/gen"
"github.com/jackc/pgx/v5/pgtype"
"google.golang.org/protobuf/encoding/protojson"
)
func (p *Processor) processDeploy(ctx context.Context, qtx gen.Querier, block *types.Block, event nodesaleEvent) error {
valid := true
deploy := event.eventMessage.Deploy
sellerAddr := p.pubkeyToAddress(string(deploy.SellerPublicKey))
if !bytes.Equal(
[]byte(sellerAddr.EncodeAddress()),
[]byte(event.txAddress.EncodeAddress()),
) {
valid = false
}
tiers := make([][]byte, len(deploy.Tiers))
for _, tier := range deploy.Tiers {
tierJson, err := protojson.Marshal(tier)
if err != nil {
return fmt.Errorf("Failed to parse tiers to json : %w", err)
}
tiers = append(tiers, tierJson)
}
err := qtx.AddEvent(ctx, gen.AddEventParams{
TxHash: event.transaction.TxHash.String(),
TxIndex: int32(event.transaction.Index),
Action: int32(event.eventMessage.Action),
RawMessage: event.rawData,
ParsedMessage: event.eventJson,
BlockTimestamp: pgtype.Timestamp{Time: block.Header.Timestamp, Valid: true},
BlockHash: block.Header.Hash.String(),
BlockHeight: int32(block.Header.Height),
Valid: valid,
WalletAddress: event.txAddress.EncodeAddress(),
Metadata: []byte{},
})
if err != nil {
return fmt.Errorf("Failed to insert event : %w", err)
}
if valid {
err = qtx.AddNodesale(ctx, gen.AddNodesaleParams{
BlockHeight: int32(event.transaction.BlockHeight),
TxIndex: int32(event.transaction.Index),
Name: deploy.Name,
StartsAt: pgtype.Timestamp{
Time: time.Unix(int64(deploy.StartsAt), 0).UTC(),
Valid: true,
},
EndsAt: pgtype.Timestamp{
Time: time.Unix(int64(deploy.EndsAt), 0).UTC(),
Valid: true,
},
Tiers: tiers,
SellerPublicKey: string(deploy.SellerPublicKey),
MaxPerAddress: int32(deploy.MaxPerAddress),
DeployTxHash: event.transaction.TxHash.String(),
MaxDiscountPercentage: int32(deploy.MaxDiscountPercentage),
})
if err != nil {
return fmt.Errorf("Failed to insert nodesale : %w", err)
}
}
return nil
}

View File

@@ -3,13 +3,9 @@ package nodesale
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"log/slog"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
@@ -17,7 +13,6 @@ import (
"github.com/gaze-network/indexer-network/core/indexer"
"github.com/gaze-network/indexer-network/core/types"
"github.com/gaze-network/indexer-network/pkg/logger"
"github.com/jackc/pgx/v5/pgtype"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
@@ -173,138 +168,6 @@ func (p *Processor) parseTransactions(ctx context.Context, transactions []*types
return events, nil
}
func (p *Processor) pubkeyToAddress(pubkey string) btcutil.Address {
pubKeyBytes, _ := hex.DecodeString(pubkey)
pubKey, _ := btcec.ParsePubKey(pubKeyBytes)
sellerAddr, _ := btcutil.NewAddressTaproot(schnorr.SerializePubKey(pubKey), p.network.ChainParams())
return sellerAddr
}
func (p *Processor) processDeploy(ctx context.Context, qtx gen.Querier, block *types.Block, event nodesaleEvent) error {
valid := true
deploy := event.eventMessage.Deploy
sellerAddr := p.pubkeyToAddress(string(deploy.SellerPublicKey))
if !bytes.Equal(
[]byte(sellerAddr.EncodeAddress()),
[]byte(event.txAddress.EncodeAddress()),
) {
valid = false
}
tiers := make([][]byte, len(deploy.Tiers))
for _, tier := range deploy.Tiers {
tierJson, err := protojson.Marshal(tier)
if err != nil {
return fmt.Errorf("Failed to parse tiers to json : %w", err)
}
tiers = append(tiers, tierJson)
}
err := qtx.AddEvent(ctx, gen.AddEventParams{
TxHash: event.transaction.TxHash.String(),
TxIndex: int32(event.transaction.Index),
Action: int32(event.eventMessage.Action),
RawMessage: event.rawData,
ParsedMessage: event.eventJson,
BlockTimestamp: pgtype.Timestamp{Time: block.Header.Timestamp, Valid: true},
BlockHash: block.Header.Hash.String(),
BlockHeight: int32(block.Header.Height),
Valid: valid,
WalletAddress: event.txAddress.EncodeAddress(),
})
if err != nil {
return fmt.Errorf("Failed to insert event : %w", err)
}
if valid {
err = qtx.AddNodesale(ctx, gen.AddNodesaleParams{
BlockHeight: int32(event.transaction.BlockHeight),
TxIndex: int32(event.transaction.Index),
Name: deploy.Name,
StartsAt: pgtype.Timestamp{
Time: time.Unix(int64(deploy.StartsAt), 0).UTC(),
Valid: true,
},
EndsAt: pgtype.Timestamp{
Time: time.Unix(int64(deploy.EndsAt), 0).UTC(),
Valid: true,
},
Tiers: tiers,
SellerPublicKey: string(deploy.SellerPublicKey),
MaxPerAddress: int32(deploy.MaxPerAddress),
DeployTxHash: event.transaction.TxHash.String(),
})
if err != nil {
return fmt.Errorf("Failed to insert nodesale : %w", err)
}
}
return nil
}
func (p *Processor) processDelegate(ctx context.Context, qtx gen.Querier, block *types.Block, event nodesaleEvent) error {
valid := true
delegate := event.eventMessage.Delegate
nodeIds := make([]int32, len(delegate.NodeIDs))
for _, id := range delegate.NodeIDs {
nodeIds = append(nodeIds, int32(id))
}
nodes, err := qtx.GetNodes(ctx, gen.GetNodesParams{
SaleBlock: int32(delegate.DeployID.Block),
SaleTxIndex: int32(delegate.DeployID.TxIndex),
NodeIds: nodeIds,
})
if err != nil {
return fmt.Errorf("Failed to get nodes : %w", err)
}
if len(nodeIds) != len(nodes) {
valid = false
}
for _, node := range nodes {
ownerAddress := p.pubkeyToAddress(node.OwnerPublicKey)
if !bytes.Equal(
[]byte(ownerAddress.EncodeAddress()),
[]byte(event.txAddress.EncodeAddress()),
) {
valid = false
}
}
err = qtx.AddEvent(ctx, gen.AddEventParams{
TxHash: event.transaction.TxHash.String(),
TxIndex: int32(event.transaction.Index),
Action: int32(event.eventMessage.Action),
RawMessage: event.rawData,
ParsedMessage: event.eventJson,
BlockTimestamp: pgtype.Timestamp{Time: block.Header.Timestamp, Valid: true},
BlockHash: block.Header.Hash.String(),
BlockHeight: int32(block.Header.Height),
Valid: valid,
WalletAddress: event.txAddress.EncodeAddress(),
})
if err != nil {
return fmt.Errorf("Failed to insert event : %w", err)
}
if valid {
_, err = qtx.SetDelegates(ctx, gen.SetDelegatesParams{
SaleBlock: int32(event.transaction.BlockHeight),
SaleTxIndex: int32(event.transaction.Index),
Delegatee: string(delegate.DelegateePublicKey),
NodeIds: nodeIds,
})
if err != nil {
return fmt.Errorf("Failed to set delegate : %w", err)
}
}
return nil
}
func (p *Processor) processPurchase(ctx context.Context, qtx gen.Querier, block *types.Block, event nodesaleEvent) error {
panic("unimplemented")
}
// Process implements indexer.Processor.
func (p *Processor) Process(ctx context.Context, inputs []*types.Block) error {
for _, block := range inputs {
@@ -313,7 +176,6 @@ func (p *Processor) Process(ctx context.Context, inputs []*types.Block) error {
if err != nil {
return fmt.Errorf("Invalid data from bitcoin client : %w", err)
}
// events[]
// open transaction
tx, err := p.repository.Db.Begin(ctx)
@@ -353,16 +215,11 @@ func (p *Processor) Process(ctx context.Context, inputs []*types.Block) error {
}
}
}
// close transaction
err = tx.Commit(ctx)
if err != nil {
return fmt.Errorf("Failed to commit transaction : %w", err)
}
/*if err := p.processBlock(ctx, block); err != nil {
return fmt.Errorf("Process block %d failed. : %w", block.Header.Height, err)
}*/
}
return nil
}

View File

@@ -147,12 +147,13 @@ type ActionDeploy struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
StartsAt uint32 `protobuf:"varint,2,opt,name=startsAt,proto3" json:"startsAt,omitempty"`
EndsAt uint32 `protobuf:"varint,3,opt,name=endsAt,proto3" json:"endsAt,omitempty"`
Tiers []*Tier `protobuf:"bytes,4,rep,name=tiers,proto3" json:"tiers,omitempty"`
SellerPublicKey []byte `protobuf:"bytes,5,opt,name=sellerPublicKey,proto3" json:"sellerPublicKey,omitempty"`
MaxPerAddress uint32 `protobuf:"varint,6,opt,name=maxPerAddress,proto3" json:"maxPerAddress,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
StartsAt uint32 `protobuf:"varint,2,opt,name=startsAt,proto3" json:"startsAt,omitempty"`
EndsAt uint32 `protobuf:"varint,3,opt,name=endsAt,proto3" json:"endsAt,omitempty"`
Tiers []*Tier `protobuf:"bytes,4,rep,name=tiers,proto3" json:"tiers,omitempty"`
SellerPublicKey []byte `protobuf:"bytes,5,opt,name=sellerPublicKey,proto3" json:"sellerPublicKey,omitempty"`
MaxPerAddress uint32 `protobuf:"varint,6,opt,name=maxPerAddress,proto3" json:"maxPerAddress,omitempty"`
MaxDiscountPercentage uint32 `protobuf:"varint,7,opt,name=maxDiscountPercentage,proto3" json:"maxDiscountPercentage,omitempty"`
}
func (x *ActionDeploy) Reset() {
@@ -229,6 +230,13 @@ func (x *ActionDeploy) GetMaxPerAddress() uint32 {
return 0
}
func (x *ActionDeploy) GetMaxDiscountPercentage() uint32 {
if x != nil {
return x.MaxDiscountPercentage
}
return 0
}
type Tier struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -355,7 +363,7 @@ type PurchasePayload struct {
DeployID *ActionID `protobuf:"bytes,1,opt,name=deployID,proto3" json:"deployID,omitempty"`
BuyerPublicKey []byte `protobuf:"bytes,2,opt,name=buyerPublicKey,proto3" json:"buyerPublicKey,omitempty"`
NodeIDs []uint32 `protobuf:"varint,3,rep,packed,name=nodeIDs,proto3" json:"nodeIDs,omitempty"`
TotalAmount uint64 `protobuf:"varint,4,opt,name=totalAmount,proto3" json:"totalAmount,omitempty"`
TotalAmountSat int64 `protobuf:"varint,4,opt,name=totalAmountSat,proto3" json:"totalAmountSat,omitempty"`
}
func (x *PurchasePayload) Reset() {
@@ -411,9 +419,9 @@ func (x *PurchasePayload) GetNodeIDs() []uint32 {
return nil
}
func (x *PurchasePayload) GetTotalAmount() uint64 {
func (x *PurchasePayload) GetTotalAmountSat() int64 {
if x != nil {
return x.TotalAmount
return x.TotalAmountSat
}
return 0
}
@@ -559,7 +567,7 @@ var file_modules_nodesale_protobuf_nodesale_proto_rawDesc = []byte{
0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f,
0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x70, 0x75, 0x72, 0x63, 0x68,
0x61, 0x73, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65,
0x22, 0xcc, 0x01, 0x0a, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x70, 0x6c, 0x6f,
0x22, 0x82, 0x02, 0x0a, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x70, 0x6c, 0x6f,
0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x41,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x41,
@@ -571,53 +579,57 @@ var file_modules_nodesale_protobuf_nodesale_proto_rawDesc = []byte{
0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78,
0x50, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22,
0x5e, 0x0a, 0x04, 0x54, 0x69, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x63, 0x65,
0x53, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x69, 0x63, 0x65,
0x53, 0x61, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78,
0x50, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d,
0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22,
0x6f, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73,
0x65, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x61, 0x6c, 0x65, 0x2e, 0x50, 0x75,
0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70,
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72,
0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x0f, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
0x22, 0xa5, 0x01, 0x0a, 0x0f, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x50, 0x61, 0x79,
0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x44,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x61, 0x6c,
0x65, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6c,
0x6f, 0x79, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x62, 0x75, 0x79, 0x65, 0x72, 0x50, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x62, 0x75,
0x79, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07,
0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x6e,
0x6f, 0x64, 0x65, 0x49, 0x44, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41,
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x6f, 0x74,
0x61, 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20,
0x01, 0x28, 0x04, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x78, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44,
0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x67,
0x61, 0x74, 0x65, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x65, 0x50, 0x75,
0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49,
0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44,
0x73, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x61, 0x6c, 0x65, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49,
0x44, 0x2a, 0x45, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x11, 0x0a, 0x0d, 0x41,
0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x10, 0x00, 0x12, 0x13,
0x0a, 0x0f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53,
0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45,
0x4c, 0x45, 0x47, 0x41, 0x54, 0x45, 0x10, 0x02, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x2d, 0x6e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x72, 0x2d, 0x6e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x6e, 0x6f, 0x64, 0x65,
0x73, 0x61, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12,
0x34, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x65,
0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15,
0x6d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x65, 0x72, 0x63, 0x65,
0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x5e, 0x0a, 0x04, 0x54, 0x69, 0x65, 0x72, 0x12, 0x1a, 0x0a,
0x08, 0x70, 0x72, 0x69, 0x63, 0x65, 0x53, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x08, 0x70, 0x72, 0x69, 0x63, 0x65, 0x53, 0x61, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d,
0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12,
0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x65, 0x72, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x6f, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50,
0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x73,
0x61, 0x6c, 0x65, 0x2e, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x50, 0x61, 0x79, 0x6c,
0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x28, 0x0a, 0x0f,
0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x53, 0x69, 0x67,
0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xab, 0x01, 0x0a, 0x0f, 0x50, 0x75, 0x72, 0x63, 0x68,
0x61, 0x73, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x65,
0x70, 0x6c, 0x6f, 0x79, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6e,
0x6f, 0x64, 0x65, 0x73, 0x61, 0x6c, 0x65, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44,
0x52, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x0e, 0x62, 0x75,
0x79, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0e, 0x62, 0x75, 0x79, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20,
0x03, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x73, 0x12, 0x26, 0x0a, 0x0e,
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x61, 0x74, 0x18, 0x04,
0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6d, 0x6f, 0x75, 0x6e,
0x74, 0x53, 0x61, 0x74, 0x22, 0x3a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44,
0x12, 0x14, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65,
0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78,
0x22, 0x8a, 0x01, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x6c, 0x65, 0x67,
0x61, 0x74, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x65,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x12, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02,
0x20, 0x03, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x73, 0x12, 0x2e, 0x0a,
0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x12, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x61, 0x6c, 0x65, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x49, 0x44, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x49, 0x44, 0x2a, 0x45, 0x0a,
0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x43, 0x54, 0x49, 0x4f,
0x4e, 0x5f, 0x44, 0x45, 0x50, 0x4c, 0x4f, 0x59, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x43,
0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, 0x45, 0x10, 0x01, 0x12,
0x13, 0x0a, 0x0f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41,
0x54, 0x45, 0x10, 0x02, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x7a, 0x65, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f,
0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x72, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f,
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x61, 0x6c, 0x65,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (

View File

@@ -25,6 +25,7 @@ message ActionDeploy {
repeated Tier tiers = 4;
bytes sellerPublicKey = 5;
uint32 maxPerAddress = 6;
uint32 maxDiscountPercentage = 7;
}
message Tier {
@@ -42,7 +43,7 @@ message PurchasePayload {
ActionID deployID = 1;
bytes buyerPublicKey = 2;
repeated uint32 nodeIDs = 3;
uint64 totalAmount = 4;
int64 totalAmountSat = 4;
}
message ActionID {

View File

@@ -0,0 +1,16 @@
package nodesale
import (
"encoding/hex"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
)
func (p *Processor) pubkeyToAddress(pubkey string) btcutil.Address {
pubKeyBytes, _ := hex.DecodeString(pubkey)
pubKey, _ := btcec.ParsePubKey(pubKeyBytes)
sellerAddr, _ := btcutil.NewAddressTaproot(schnorr.SerializePubKey(pubKey), p.network.ChainParams())
return sellerAddr
}

View File

@@ -0,0 +1,245 @@
package nodesale
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"slices"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/txscript"
"github.com/gaze-network/indexer-network/core/types"
"github.com/gaze-network/indexer-network/modules/nodesale/protobuf"
"github.com/gaze-network/indexer-network/modules/nodesale/repository/postgres/gen"
"github.com/jackc/pgx/v5/pgtype"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
func (p *Processor) processPurchase(ctx context.Context, qtx gen.Querier, block *types.Block, event nodesaleEvent) error {
valid := true
purchase := event.eventMessage.Purchase
payload := purchase.Payload
buyerAddr := p.pubkeyToAddress(string(payload.BuyerPublicKey))
if !bytes.Equal(
[]byte(buyerAddr.EncodeAddress()),
[]byte(event.txAddress.EncodeAddress()),
) {
valid = false
}
var deploy *gen.NodeSale
if valid {
// check node existed
deploys, err := qtx.GetNodesale(ctx, gen.GetNodesaleParams{
BlockHeight: int32(payload.DeployID.Block),
TxIndex: int32(payload.DeployID.TxIndex),
})
if err != nil {
return fmt.Errorf("Failed to Get nodesale : %w", err)
}
if len(deploys) < 1 {
valid = false
} else {
deploy = &deploys[0]
}
}
if valid {
// check timestamp
timestamp := block.Header.Timestamp
if timestamp.UTC().Before(deploy.StartsAt.Time.UTC()) ||
timestamp.UTC().After(deploy.EndsAt.Time.UTC()) {
valid = false
}
}
if valid {
// verified signature
payloadBytes, _ := proto.Marshal(payload)
signatureBytes, _ := hex.DecodeString(string(purchase.SellerSignature))
signature, _ := ecdsa.ParseSignature(signatureBytes)
hash := sha256.Sum256(payloadBytes)
pubkeyBytes, _ := hex.DecodeString(deploy.SellerPublicKey)
pubKey, _ := btcec.ParsePubKey(pubkeyBytes)
verified := signature.Verify(hash[:], pubKey)
if !verified {
valid = false
}
}
var tiers []protobuf.Tier
var buyingTiersCount []uint32
nodeIdToTier := make(map[uint32]int32, 1)
if valid {
// valid nodeID tier
tiers = make([]protobuf.Tier, len(deploy.Tiers))
for i, tierJson := range deploy.Tiers {
tier := &tiers[i]
err := protojson.Unmarshal(tierJson, tier)
if err != nil {
return fmt.Errorf("Failed to decode tiers json : %w", err)
}
}
slices.Sort(payload.NodeIDs)
buyingTiersCount = make([]uint32, len(tiers))
var currentTier int32 = -1
var tierSum uint32 = 0
for _, nodeId := range payload.NodeIDs {
for nodeId >= tierSum && currentTier < int32(len(tiers)-1) {
currentTier++
tierSum += tiers[currentTier].Limit
}
if nodeId < tierSum {
buyingTiersCount[currentTier]++
nodeIdToTier[nodeId] = currentTier
} else {
valid = false
}
}
}
if valid {
// valid unpurchased node ID
nodeIds := make([]int32, len(payload.NodeIDs))
for i, id := range payload.NodeIDs {
nodeIds[i] = int32(id)
}
nodes, err := qtx.GetNodes(ctx, gen.GetNodesParams{
SaleBlock: int32(payload.DeployID.Block),
SaleTxIndex: int32(payload.DeployID.TxIndex),
NodeIds: nodeIds,
})
if err != nil {
return fmt.Errorf("Failed to Get nodes : %w", err)
}
if len(nodes) > 0 {
valid = false
}
}
var txPaid int64 = 0
if valid {
// get total amount paid to seller
sellerAddr := p.pubkeyToAddress(deploy.SellerPublicKey)
for _, txOut := range event.transaction.TxOut {
_, txOutAddrs, _, _ := txscript.ExtractPkScriptAddrs(txOut.PkScript, p.network.ChainParams())
if len(txOutAddrs) == 1 && bytes.Equal(
[]byte(sellerAddr.EncodeAddress()),
[]byte(txOutAddrs[0].EncodeAddress()),
) {
txPaid += txOut.Value
}
}
// total amount paid is greater than report paid
if txPaid < payload.TotalAmountSat {
valid = false
}
}
if valid {
// calculate total price
var totalPrice int64 = 0
for i := 0; i < len(tiers); i++ {
totalPrice += int64(buyingTiersCount[i] * tiers[i].PriceSat)
}
// report paid is greater than max discounted total price
maxDiscounted := totalPrice * (100 - int64(deploy.MaxDiscountPercentage))
decimal := maxDiscounted % 100
maxDiscounted /= 100
if decimal%100 >= 50 {
maxDiscounted++
}
if payload.TotalAmountSat < maxDiscounted {
valid = false
}
}
var buyerOwnedNodes []gen.Node
if valid {
var err error
// check node limit
// get all selled by seller and owned by buyer
buyerOwnedNodes, err = qtx.GetNodesByOwner(ctx, gen.GetNodesByOwnerParams{
SaleBlock: deploy.BlockHeight,
SaleTxIndex: deploy.TxIndex,
OwnerPublicKey: string(payload.BuyerPublicKey),
})
if err != nil {
return fmt.Errorf("Failed to GetNodesByOwner : %w", err)
}
if len(buyerOwnedNodes) > int(deploy.MaxPerAddress) {
valid = false
}
}
if valid {
// check limit
// count each tiers
// check limited for each tier
ownedTiersCount := make([]uint32, len(tiers))
currentTier := -1
var tierSum uint32 = 0
for _, node := range buyerOwnedNodes {
for uint32(node.TierIndex) >= tierSum && currentTier < len(tiers)-1 {
currentTier++
tierSum += tiers[currentTier].Limit
}
if uint32(node.TierIndex) < tierSum {
ownedTiersCount[currentTier]++
} else {
valid = false
}
}
for i := 0; i < len(tiers); i++ {
ownedTiersCount[i] += buyingTiersCount[i]
if ownedTiersCount[i] > tiers[i].Limit {
valid = false
break
}
}
}
err := qtx.AddEvent(ctx, gen.AddEventParams{
TxHash: event.transaction.TxHash.String(),
TxIndex: int32(event.transaction.Index),
Action: int32(event.eventMessage.Action),
RawMessage: event.rawData,
ParsedMessage: event.eventJson,
BlockTimestamp: pgtype.Timestamp{Time: block.Header.Timestamp, Valid: true},
BlockHash: block.Header.Hash.String(),
BlockHeight: int32(block.Header.Height),
Valid: valid,
WalletAddress: event.txAddress.EncodeAddress(),
Metadata: []byte{},
})
if err != nil {
return fmt.Errorf("Failed to insert event : %w", err)
}
if valid {
// add to node
for _, nodeId := range payload.NodeIDs {
err := qtx.AddNode(ctx, gen.AddNodeParams{
SaleBlock: int32(event.transaction.BlockHeight),
SaleTxIndex: int32(event.transaction.Index),
NodeID: int32(nodeId),
TierIndex: nodeIdToTier[nodeId],
DelegatedTo: pgtype.Text{Valid: false},
OwnerPublicKey: string(payload.BuyerPublicKey),
PurchaseTxHash: event.transaction.TxHash.String(),
DelegateTxHash: pgtype.Text{Valid: false},
})
if err != nil {
return fmt.Errorf("Failed to insert node : %w", err)
}
}
}
return nil
}

View File

@@ -13,8 +13,8 @@ import (
const addEvent = `-- name: AddEvent :exec
INSERT INTO events("tx_hash", "block_height", "tx_index", "wallet_address", "valid", "action",
"raw_message", "parsed_message", "block_timestamp", "block_hash")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
"raw_message", "parsed_message", "block_timestamp", "block_hash", "metadata")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
`
type AddEventParams struct {
@@ -28,6 +28,7 @@ type AddEventParams struct {
ParsedMessage []byte
BlockTimestamp pgtype.Timestamp
BlockHash string
Metadata []byte
}
func (q *Queries) AddEvent(ctx context.Context, arg AddEventParams) error {
@@ -42,6 +43,7 @@ func (q *Queries) AddEvent(ctx context.Context, arg AddEventParams) error {
arg.ParsedMessage,
arg.BlockTimestamp,
arg.BlockHash,
arg.Metadata,
)
return err
}

View File

@@ -33,20 +33,21 @@ type Node struct {
SaleTxIndex int32
NodeID int32
TierIndex int32
DelegatedTo string
DelegatedTo pgtype.Text
OwnerPublicKey string
PurchaseTxHash string
DelegateTxHash string
DelegateTxHash pgtype.Text
}
type NodeSale struct {
BlockHeight int32
TxIndex int32
Name string
StartsAt pgtype.Timestamp
EndsAt pgtype.Timestamp
Tiers [][]byte
SellerPublicKey string
MaxPerAddress int32
DeployTxHash string
BlockHeight int32
TxIndex int32
Name string
StartsAt pgtype.Timestamp
EndsAt pgtype.Timestamp
Tiers [][]byte
SellerPublicKey string
MaxPerAddress int32
DeployTxHash string
MaxDiscountPercentage int32
}

View File

@@ -7,8 +7,40 @@ package gen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const addNode = `-- name: AddNode :exec
INSERT INTO nodes(sale_block, sale_tx_index, node_id, tier_index, delegated_to, owner_public_key, purchase_tx_hash, delegate_tx_hash)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`
type AddNodeParams struct {
SaleBlock int32
SaleTxIndex int32
NodeID int32
TierIndex int32
DelegatedTo pgtype.Text
OwnerPublicKey string
PurchaseTxHash string
DelegateTxHash pgtype.Text
}
func (q *Queries) AddNode(ctx context.Context, arg AddNodeParams) error {
_, err := q.db.Exec(ctx, addNode,
arg.SaleBlock,
arg.SaleTxIndex,
arg.NodeID,
arg.TierIndex,
arg.DelegatedTo,
arg.OwnerPublicKey,
arg.PurchaseTxHash,
arg.DelegateTxHash,
)
return err
}
const clearDelegate = `-- name: ClearDelegate :execrows
UPDATE nodes
SET "delegated_to" = NULL
@@ -66,6 +98,50 @@ func (q *Queries) GetNodes(ctx context.Context, arg GetNodesParams) ([]Node, err
return items, nil
}
const getNodesByOwner = `-- name: GetNodesByOwner :many
SELECT sale_block, sale_tx_index, node_id, tier_index, delegated_to, owner_public_key, purchase_tx_hash, delegate_tx_hash
FROM nodes
WHERE sale_block = $1 AND
sale_tx_index = $2 AND
owner_public_key = $3
ORDER BY tier_index
`
type GetNodesByOwnerParams struct {
SaleBlock int32
SaleTxIndex int32
OwnerPublicKey string
}
func (q *Queries) GetNodesByOwner(ctx context.Context, arg GetNodesByOwnerParams) ([]Node, error) {
rows, err := q.db.Query(ctx, getNodesByOwner, arg.SaleBlock, arg.SaleTxIndex, arg.OwnerPublicKey)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Node
for rows.Next() {
var i Node
if err := rows.Scan(
&i.SaleBlock,
&i.SaleTxIndex,
&i.NodeID,
&i.TierIndex,
&i.DelegatedTo,
&i.OwnerPublicKey,
&i.PurchaseTxHash,
&i.DelegateTxHash,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const setDelegates = `-- name: SetDelegates :execrows
UPDATE nodes
SET delegated_to = $3
@@ -77,7 +153,7 @@ WHERE sale_block = $1 AND
type SetDelegatesParams struct {
SaleBlock int32
SaleTxIndex int32
Delegatee string
Delegatee pgtype.Text
NodeIds []int32
}

View File

@@ -12,20 +12,21 @@ import (
)
const addNodesale = `-- name: AddNodesale :exec
INSERT INTO node_sales("block_height", "tx_index", "name", "starts_at", "ends_at", "tiers", "seller_public_key", "max_per_address", "deploy_tx_hash")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9)
INSERT INTO node_sales("block_height", "tx_index", "name", "starts_at", "ends_at", "tiers", "seller_public_key", "max_per_address", "deploy_tx_hash", "max_discount_percentage")
VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
`
type AddNodesaleParams struct {
BlockHeight int32
TxIndex int32
Name string
StartsAt pgtype.Timestamp
EndsAt pgtype.Timestamp
Tiers [][]byte
SellerPublicKey string
MaxPerAddress int32
DeployTxHash string
BlockHeight int32
TxIndex int32
Name string
StartsAt pgtype.Timestamp
EndsAt pgtype.Timestamp
Tiers [][]byte
SellerPublicKey string
MaxPerAddress int32
DeployTxHash string
MaxDiscountPercentage int32
}
func (q *Queries) AddNodesale(ctx context.Context, arg AddNodesaleParams) error {
@@ -39,6 +40,50 @@ func (q *Queries) AddNodesale(ctx context.Context, arg AddNodesaleParams) error
arg.SellerPublicKey,
arg.MaxPerAddress,
arg.DeployTxHash,
arg.MaxDiscountPercentage,
)
return err
}
const getNodesale = `-- name: GetNodesale :many
SELECT block_height, tx_index, name, starts_at, ends_at, tiers, seller_public_key, max_per_address, deploy_tx_hash, max_discount_percentage
FROM node_sales
WHERE block_height = $1 AND
tx_index = $2
`
type GetNodesaleParams struct {
BlockHeight int32
TxIndex int32
}
func (q *Queries) GetNodesale(ctx context.Context, arg GetNodesaleParams) ([]NodeSale, error) {
rows, err := q.db.Query(ctx, getNodesale, arg.BlockHeight, arg.TxIndex)
if err != nil {
return nil, err
}
defer rows.Close()
var items []NodeSale
for rows.Next() {
var i NodeSale
if err := rows.Scan(
&i.BlockHeight,
&i.TxIndex,
&i.Name,
&i.StartsAt,
&i.EndsAt,
&i.Tiers,
&i.SellerPublicKey,
&i.MaxPerAddress,
&i.DeployTxHash,
&i.MaxDiscountPercentage,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@@ -11,11 +11,14 @@ import (
type Querier interface {
AddBlock(ctx context.Context, arg AddBlockParams) error
AddEvent(ctx context.Context, arg AddEventParams) error
AddNode(ctx context.Context, arg AddNodeParams) error
AddNodesale(ctx context.Context, arg AddNodesaleParams) error
ClearDelegate(ctx context.Context) (int64, error)
GetBlock(ctx context.Context, blockHeight int32) (Block, error)
GetLastProcessedBlock(ctx context.Context) (Block, error)
GetNodes(ctx context.Context, arg GetNodesParams) ([]Node, error)
GetNodesByOwner(ctx context.Context, arg GetNodesByOwnerParams) ([]Node, error)
GetNodesale(ctx context.Context, arg GetNodesaleParams) ([]NodeSale, error)
RemoveBlockFrom(ctx context.Context, fromBlock int32) (int64, error)
RemoveEventsFromBlock(ctx context.Context, fromBlock int32) (int64, error)
SetDelegates(ctx context.Context, arg SetDelegatesParams) (int64, error)