Skip to content

Commit

Permalink
Support for confiscation
Browse files Browse the repository at this point in the history
  • Loading branch information
galt-tr committed Jan 14, 2024
1 parent da7f435 commit 157d005
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 22 deletions.
31 changes: 26 additions & 5 deletions app/config/mocks/mocks.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
// Package mocks is a generated mocking package for the mocks
package mocks

import "context"
import (
"context"

"github.com/libsv/go-bn/models"
)

// Node is a mock type for the SVNode interface
type Node struct {
Expand All @@ -11,10 +15,11 @@ type Node struct {
RPCUser string

// Functions
BanPeerFunc func(ctx context.Context, peer string) error
InvalidateBlockFunc func(ctx context.Context, hash string) error
UnbanPeerFunc func(ctx context.Context, peer string) error

BanPeerFunc func(ctx context.Context, peer string) error
InvalidateBlockFunc func(ctx context.Context, hash string) error
UnbanPeerFunc func(ctx context.Context, peer string) error
AddToConsensusBlacklistFunc func(ctx context.Context, funds []models.Fund) (*models.AddToConsensusBlacklistResponse, error)
AddToConfiscationTransactionWhitelistFunc func(ctx context.Context, tx []models.ConfiscationTransactionDetails) (*models.AddToConfiscationTransactionWhitelistResponse, error)
// Add additional fields if needed to track calls or results
}

Expand Down Expand Up @@ -57,3 +62,19 @@ func (n *Node) UnbanPeer(ctx context.Context, peer string) error {
}
return nil
}

// AddToConsensusBlacklist will call the AddToConsensusBlacklistFunc if not nil, otherwise return nil
func (n *Node) AddToConsensusBlacklist(ctx context.Context, funds []models.Fund) (*models.AddToConsensusBlacklistResponse, error) {
if n.AddToConsensusBlacklistFunc != nil {
return n.AddToConsensusBlacklistFunc(ctx, funds)
}
return nil, nil
}

// AddToConfiscationTransactionWhitelist will call the AddToConfiscationTransactionWhitelistFunc if not nil, otherwise return nil
func (n *Node) AddToConfiscationTransactionWhitelist(ctx context.Context, tx []models.ConfiscationTransactionDetails) (*models.AddToConfiscationTransactionWhitelistResponse, error) {
if n.AddToConfiscationTransactionWhitelistFunc != nil {
return n.AddToConfiscationTransactionWhitelistFunc(ctx, tx)
}
return nil, nil
}
16 changes: 16 additions & 0 deletions app/config/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package config
import (
"context"

"github.com/libsv/go-bn/models"

"github.com/bitcoin-sv/alert-system/app/config/mocks"
"github.com/libsv/go-bn"
)
Expand All @@ -15,6 +17,8 @@ type NodeInterface interface {
GetRPCUser() string
InvalidateBlock(ctx context.Context, hash string) error
UnbanPeer(ctx context.Context, peer string) error
AddToConsensusBlacklist(ctx context.Context, funds []models.Fund) (*models.AddToConsensusBlacklistResponse, error)
AddToConfiscationTransactionWhitelist(ctx context.Context, tx []models.ConfiscationTransactionDetails) (*models.AddToConfiscationTransactionWhitelistResponse, error)
}

// NewNodeConfig creates a new NodeConfig struct
Expand Down Expand Up @@ -67,3 +71,15 @@ func (n *Node) UnbanPeer(ctx context.Context, peer string) error {
c := bn.NewNodeClient(bn.WithCreds(n.RPCUser, n.RPCPassword), bn.WithHost(n.RPCHost))
return c.SetBan(ctx, peer, bn.BanActionRemove, nil)
}

// AddToConsensusBlacklist adds frozen utxos to blacklist
func (n *Node) AddToConsensusBlacklist(ctx context.Context, funds []models.Fund) (*models.AddToConsensusBlacklistResponse, error) {
c := bn.NewNodeClient(bn.WithCreds(n.RPCUser, n.RPCPassword), bn.WithHost(n.RPCHost))
return c.AddToConsensusBlacklist(ctx, funds)
}

// AddToConfiscationTransactionWhitelist adds confiscation transactions to the whitelist
func (n *Node) AddToConfiscationTransactionWhitelist(ctx context.Context, tx []models.ConfiscationTransactionDetails) (*models.AddToConfiscationTransactionWhitelistResponse, error) {
c := bn.NewNodeClient(bn.WithCreds(n.RPCUser, n.RPCPassword), bn.WithHost(n.RPCHost))
return c.AddToConfiscationTransactionWhitelist(ctx, tx)
}
2 changes: 1 addition & 1 deletion app/models/alert_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (m *AlertMessage) ProcessAlertMessage() AlertMessageInterface {
AlertMessage: *m,
}
case AlertTypeConfiscateUtxo:
return &AlertMessageConfiscateUtxo{
return &AlertMessageConfiscateTransaction{
AlertMessage: *m,
}
case AlertTypeBanPeer:
Expand Down
51 changes: 45 additions & 6 deletions app/models/alert_message_confiscate_utxo.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,58 @@
package models

import "context"
import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"

// AlertMessageConfiscateUtxo is a confiscate utxo alert
type AlertMessageConfiscateUtxo struct {
"github.com/libsv/go-bn/models"
)

// AlertMessageConfiscateTransaction is a confiscate utxo alert
type AlertMessageConfiscateTransaction struct {
AlertMessage
// TODO finish building out this alert type
Transactions []models.ConfiscationTransactionDetails
}

type ConfiscateTransaction struct {

Check failure on line 18 in app/models/alert_message_confiscate_utxo.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

exported: exported type ConfiscateTransaction should have comment or be unexported (revive)
EnforceAtHeight [8]byte
Id [32]byte

Check failure on line 20 in app/models/alert_message_confiscate_utxo.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

var-naming: struct field Id should be ID (revive)
}

// Read reads the alert
func (a *AlertMessageConfiscateUtxo) Read(_ []byte) error {
func (a *AlertMessageConfiscateTransaction) Read(raw []byte) error {
a.Config().Services.Log.Infof("%x", raw)
if len(raw) < 40 {
return fmt.Errorf("confiscation alert is less than 41 bytes")
}
if len(raw)%40 != 0 {
return fmt.Errorf("confiscation alert is not a multiple of 41 bytes")
}
txCount := len(raw) / 40
details := []models.ConfiscationTransactionDetails{}
for i := 0; i < txCount; i++ {
tx := ConfiscateTransaction{
EnforceAtHeight: [8]byte(raw[:8]),
Id: [32]byte(raw[8:40]),
}
detail := models.ConfiscationTransactionDetails{
ConfiscationTransaction: models.ConfiscationTransaction{
EnforceAtHeight: int64(binary.LittleEndian.Uint64(tx.EnforceAtHeight[:])),
Hex: hex.EncodeToString(tx.Id[:]),
},
}
details = append(details, detail)

Check failure on line 45 in app/models/alert_message_confiscate_utxo.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

SA4010: this result of append is never used, except maybe in other appends (staticcheck)
raw = raw[40:]
}
return nil
}

// Do executes the alert
func (a *AlertMessageConfiscateUtxo) Do(_ context.Context) error {
func (a *AlertMessageConfiscateTransaction) Do(ctx context.Context) error {
_, err := a.Config().Services.Node.AddToConfiscationTransactionWhitelist(ctx, a.Transactions)
if err != nil {
return err
}
return nil
}
64 changes: 60 additions & 4 deletions app/models/alert_message_freeze_utxo.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,75 @@
package models

import "context"
import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"

"github.com/libsv/go-bn/models"
)

// AlertMessageFreezeUtxo is the message for freezing UTXOs
type AlertMessageFreezeUtxo struct {
AlertMessage
// TODO finish building out this alert type
Funds []models.Fund
}

type Fund struct {

Check failure on line 18 in app/models/alert_message_freeze_utxo.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

exported: exported type Fund should have comment or be unexported (revive)
TransactionOutId [32]byte

Check failure on line 19 in app/models/alert_message_freeze_utxo.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

var-naming: struct field TransactionOutId should be TransactionOutID (revive)
Vout [8]byte
EnforceAtHeightStart [8]byte
EnforceAtHeightEnd [8]byte
PolicyExpiresWithConsensus bool
}

// Read reads the message
func (a *AlertMessageFreezeUtxo) Read(_ []byte) error {
func (a *AlertMessageFreezeUtxo) Read(raw []byte) error {
if len(raw) < 57 {
return fmt.Errorf("freeze alert is less than 58 bytes")
}
if len(raw)%57 != 0 {
return fmt.Errorf("freeze alert is not a multiple of 58 bytes")
}
fundCount := len(raw) / 57
funds := []models.Fund{}
for i := 0; i < fundCount; i++ {
fund := Fund{
TransactionOutId: [32]byte(raw[0:32]),
Vout: [8]byte(raw[32:40]),
EnforceAtHeightStart: [8]byte(raw[40:48]),
EnforceAtHeightEnd: [8]byte(raw[48:56]),
}
enforceByte := binary.LittleEndian.Uint16(raw[56:57])

if enforceByte != uint16(0) {
fund.PolicyExpiresWithConsensus = true
}
funds = append(funds, models.Fund{
TxOut: models.TxOut{
TxId: hex.EncodeToString(fund.TransactionOutId[:]),
Vout: int(binary.LittleEndian.Uint64(fund.Vout[:])),
},
EnforceAtHeight: []models.Enforce{
{
Start: int(binary.LittleEndian.Uint64(fund.EnforceAtHeightStart[:])),
Stop: int(binary.LittleEndian.Uint64(fund.EnforceAtHeightEnd[:])),
},
},
PolicyExpiresWithConsensus: fund.PolicyExpiresWithConsensus,
})
raw = raw[57:]
}
a.Funds = funds

return nil
}

// Do performs the message
func (a *AlertMessageFreezeUtxo) Do(_ context.Context) error {
func (a *AlertMessageFreezeUtxo) Do(ctx context.Context) error {
_, err := a.Config().Services.Node.AddToConsensusBlacklist(ctx, a.Funds)
if err != nil {
return err
}
return nil
}
1 change: 1 addition & 0 deletions app/models/alert_message_unfreeze_utxo.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "context"
type AlertMessageUnfreezeUtxo struct {
AlertMessage
// TODO finish building out this alert type
Funds []Fund
}

// Read reads the message from the byte slice
Expand Down
4 changes: 2 additions & 2 deletions app/p2p/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,13 +379,13 @@ func (s *Server) Subscribe(ctx context.Context, subscriber *pubsub.Subscription,

// Perform alert action
if err = am.Do(ctx); err != nil {
s.config.Services.Log.Infof("failed to do alert action: %s", err.Error())
s.config.Services.Log.Errorf("failed to do alert action: %s", err.Error())
continue
}

// Save the alert message
if err = ak.Save(ctx); err != nil {
s.config.Services.Log.Infof("failed to save alert message: %s", err.Error())
s.config.Services.Log.Errorf("failed to save alert message: %s", err.Error())
}

s.config.Services.Log.Infof("[%s] got alert type: %d, from: %s", subscriber.Topic(), ak.GetAlertType(), msg.ReceivedFrom.String())
Expand Down
2 changes: 1 addition & 1 deletion go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 29 additions & 1 deletion hack/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package main

import (
"context"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"strings"
"time"

models2 "github.com/libsv/go-bn/models"

"github.com/bitcoin-sv/alert-system/app/models/model"

"github.com/bitcoin-sv/alert-system/app/config"
Expand Down Expand Up @@ -59,7 +63,7 @@ func main() {
case models.AlertTypeUnbanPeer:
//a = UnbanPeerAlert(*sequenceNumber, *peer)
case models.AlertTypeConfiscateUtxo:
panic(fmt.Errorf("not implemented"))
a = ConfiscateAlert(*sequenceNumber, model.WithAllDependencies(_appConfig))
case models.AlertTypeFreezeUtxo:
panic(fmt.Errorf("not implemented"))
case models.AlertTypeUnfreezeUtxo:
Expand Down Expand Up @@ -137,6 +141,30 @@ func InfoAlert(seq uint, opts ...model.Options) *models.AlertMessage {
return newAlert
}

func ConfiscateAlert(seq uint, opts ...model.Options) *models.AlertMessage {

Check failure on line 144 in hack/publish.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

exported: exported function ConfiscateAlert should have comment or be unexported (revive)
tx := models2.ConfiscationTransactionDetails{
ConfiscationTransaction: models2.ConfiscationTransaction{
Hex: "dd1b08331cf22da4d27bd1b29019a04a168805d49b48d65a7fec381eb4307d61",
EnforceAtHeight: 10000,
},
}
raw := []byte{}
enforce := [8]byte{}
binary.LittleEndian.PutUint64(enforce[:], uint64(tx.ConfiscationTransaction.EnforceAtHeight))
raw = append(raw, enforce[:]...)
by, _ := hex.DecodeString(tx.ConfiscationTransaction.Hex)
raw = append(raw, by...)
opts = append(opts, model.New())
newAlert := models.NewAlertMessage(opts...)
newAlert.SetAlertType(models.AlertTypeConfiscateUtxo)
newAlert.SetRawMessage(raw)
newAlert.SequenceNumber = uint32(seq)
newAlert.SetTimestamp(uint64(time.Now().Second()))
newAlert.SetVersion(0x01)
newAlert.SerializeData()
return newAlert
}

/*
// BanPeerAlert creates a ban peer alert
func BanPeerAlert(seq uint, peer string) alert.Alert {
Expand Down

0 comments on commit 157d005

Please sign in to comment.