Skip to content

Commit 8726af7

Browse files
committed
spent utxo tracking added
2 parents 9b12c62 + bfe7604 commit 8726af7

19 files changed

+491
-103
lines changed

README.md

+14-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ matches [other implementations](https://github.com/bitcoin/bitcoin/pull/28241#is
77

88
## Setup
99

10-
The installation process is still very manual. Will be improved based on feedback and new findings.
10+
The installation process is still very manual. Will be improved based on feedback and new findings. It is advised to look at the example [blindbit.toml](blindbit.example.toml). As new config options appear they will be listed and explained there.
11+
12+
## Breaking changes
13+
14+
- Endpoints were expanded and have a slightly different syntax now [see endpoints](#endpoints)
1115

1216
### Requirements
1317

@@ -39,9 +43,11 @@ $ <path/to/new/binary/file> --datadir <path/to/datadir/with/blindbit.toml>
3943
Note that the program will automatically default `--datadir` to `~/.blindbit-oracle` if not set.
4044
You still have to set up a config file in any case as the rpc users can't and **should** not be defaulted.
4145

46+
You can now also decide which index you want to run. This setting can be set in the config file (blindbit.toml).
47+
4248
## Known Errors
4349

44-
All known errors resolved.
50+
No known issues.
4551

4652
## Todos
4753

@@ -69,6 +75,8 @@ All known errors resolved.
6975
- [ ] Remove unnecessary panics.
7076
- [ ] Convert hardcoded serialisation assertions into constants (?)
7177
- [x] Use x-only 32 byte public keys instead of scriptPubKey
78+
- [ ] Don't create all DBs by default, only those which are needed and activated
79+
- [ ] Check import paths (SilentPaymentBackend/.../...)
7280

7381
### Low Priority
7482

@@ -80,8 +88,10 @@ All known errors resolved.
8088
```text
8189
GET("/block-height") // returns the height of the indexing server
8290
GET("/tweaks/:blockheight?dustLimit=<sat_amount>") // returns tweak data (cut-through); optional parameter dustLimit can be omitted; filtering happens per request, so virtually any amount can be specified
83-
GET("/tweak-index/:blockheight") // returns the full tweak index (no cut-through)
84-
GET("/filter/:blockheight") // returns a custom taproot only filter (the underlying data is subject to change; changing scriptPubKey to x-only pubKey)
91+
GET("/tweak-index/:blockheight?dustLimit=<sat_amount>") // returns the full tweak index (no cut-through); optional parameter dustLimit can be omitted; filtering happens per request, so virtually any amount can be specified
92+
GET("/spent-index/:blockheight") // returns the spent outpoints index (see https://github.com/setavenger/BIP0352-light-client-specification?tab=readme-ov-file#spent-utxos)
93+
GET("/filter/spent/:blockheight") // returns a filter for shortened spent outpoint hashes (see https://github.com/setavenger/BIP0352-light-client-specification?tab=readme-ov-file#filters)
94+
GET("/filter/new-utxos/:blockheight") // returns a custom taproot only filter of x-only pubkey which received funds
8595
GET("/utxos/:blockheight") // UTXO data for that block (cut down to the essentials needed to spend)
8696
```
8797

blindbit.example.toml

+20-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
host = "127.0.0.1:8000"
44

55
# Defines on which chain the wallet runs. Allowed values: main, testnet, signet, regtest.
6-
# Default: signet
6+
# default: signet
77
chain = "signet"
88

9-
# (defaults to http://127.0.0.1:8332)
9+
# default: http://127.0.0.1:8332
1010
rpc_endpoint = "http://127.0.0.1:18443"
1111

1212
# required, unless rpc_user and rpc_pass are set
@@ -28,7 +28,23 @@ max_parallel_tweak_computations = 4
2828
# (depends on max-rpc-workers of the underlying full node)
2929
max_parallel_requests = 4
3030

31-
#
3231
# optional - will only generate tweaks (still both cut-through and full-index)
33-
# default: 0 set to 1 to activate
32+
# default: 0
3433
tweaks_only = 0
34+
35+
# The base index. Only includes the tweaks. No dust filtering or cut-through possible
36+
# default: 1
37+
tweaks_full_basic = 1
38+
39+
# if this is set a full non-cut-through index will be created.
40+
# This index can be used to filter for dust (?dustLimit=). If this is active the base index will not be created.
41+
# All full index queries will be served from this with or without (?dustLimit=) set in the query.
42+
# default 0
43+
tweaks_full_with_dust_filter = 0
44+
45+
# This index applies cut-through and dust filtering.
46+
# Beware that it will be stored in addition to any full index (with or without dust) if activated.
47+
# It has more storage requirments than the simple indices.
48+
# Currently still requires tweaks_only=1.
49+
# default: 0
50+
tweaks_cut_through_with_dust_filter = 0

src/common/config.go

+32-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package common
22

33
import (
4+
"errors"
5+
"os"
6+
47
"github.com/spf13/viper"
58
)
69

@@ -23,23 +26,32 @@ func LoadConfigs(pathToConfig string) {
2326
// RPC endpoint only. Fails if others are not set
2427
viper.SetDefault("rpc_endpoint", RpcEndpoint)
2528

26-
//Others
29+
//Tweaks
2730
viper.SetDefault("tweaks_only", false)
31+
viper.SetDefault("tweaks_full_basic", true)
32+
viper.SetDefault("tweaks_full_with_dust_filter", false)
33+
viper.SetDefault("tweaks_cut_through_with_dust_filter", false)
2834

2935
/* read and set config variables */
36+
// General
37+
SyncStartHeight = viper.GetUint32("sync_start_height")
3038
Host = viper.GetString("host")
3139

40+
// Performance
3241
MaxParallelRequests = viper.GetUint16("max_parallel_requests")
3342
MaxParallelTweakComputations = viper.GetInt("max_parallel_tweak_computations")
3443

44+
// RPC
3545
RpcEndpoint = viper.GetString("rpc_endpoint")
3646
CookiePath = viper.GetString("cookie_path")
3747
RpcPass = viper.GetString("rpc_pass")
3848
RpcUser = viper.GetString("rpc_user")
3949

40-
SyncStartHeight = viper.GetUint32("sync_start_height")
41-
50+
// Tweaks
4251
TweaksOnly = viper.GetBool("tweaks_only")
52+
TweakIndexFullNoDust = viper.GetBool("tweaks_full_basic")
53+
TweakIndexFullIncludingDust = viper.GetBool("tweaks_full_with_dust_filter")
54+
TweaksCutThroughWithDust = viper.GetBool("tweaks_cut_through_with_dust_filter")
4355

4456
chainInput := viper.GetString("chain")
4557

@@ -55,4 +67,21 @@ func LoadConfigs(pathToConfig string) {
5567
default:
5668
panic("chain undefined")
5769
}
70+
71+
// todo print settings
72+
InfoLogger.Printf("tweaks_only: %t\n", TweaksOnly)
73+
InfoLogger.Printf("tweaks_full_basic: %t\n", TweakIndexFullNoDust)
74+
InfoLogger.Printf("tweaks_full_with_dust_filter: %t\n", TweakIndexFullIncludingDust)
75+
InfoLogger.Printf("tweaks_cut_through_with_dust_filter: %t\n", TweaksCutThroughWithDust)
76+
77+
if !TweakIndexFullNoDust && !TweakIndexFullIncludingDust && !TweaksCutThroughWithDust {
78+
WarningLogger.Println("no tweaks are being collected, all tweak settings were set to 0")
79+
WarningLogger.Println("make sure your configuration loaded correctly, check example blindbit.toml for configuration")
80+
}
81+
82+
if TweaksCutThroughWithDust && TweaksOnly {
83+
err := errors.New("cut through requires tweaks_only to be set to 1")
84+
ErrorLogger.Println(err)
85+
os.Exit(1)
86+
}
5887
}

src/common/types/tweak.go

+14-12
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import (
88
"errors"
99
)
1010

11+
const TweakDataLength = 33
12+
1113
type Tweak struct {
1214
BlockHash string `json:"block_hash"`
1315
// BlockHeight todo not really used at the moment, could be added on a per request basis in the API handler
14-
BlockHeight uint32 `json:"block_height"`
15-
Txid string `json:"txid"`
16-
Data [33]byte `json:"data"`
16+
BlockHeight uint32 `json:"block_height"`
17+
Txid string `json:"txid"`
18+
TweakData [TweakDataLength]byte `json:"tweak_data"`
1719
// HighestValue indicates the value of the UTXO with the most value for a specific tweak
1820
HighestValue uint64
1921
}
@@ -30,7 +32,7 @@ func (v *Tweak) SerialiseKey() ([]byte, error) {
3032
func (v *Tweak) SerialiseData() ([]byte, error) {
3133
var buf bytes.Buffer
3234

33-
buf.Write(v.Data[:])
35+
buf.Write(v.TweakData[:])
3436

3537
err := binary.Write(&buf, binary.BigEndian, v.HighestValue)
3638
if err != nil {
@@ -53,19 +55,19 @@ func (v *Tweak) DeSerialiseKey(key []byte) error {
5355
}
5456

5557
func (v *Tweak) DeSerialiseData(data []byte) error {
56-
if len(data) != 33 && len(data) != 33+8 {
58+
// todo why did we check both dusted and non dusted
59+
if len(data) != TweakDataLength+8 {
5760
common.ErrorLogger.Printf("wrong data length: %+v", data)
5861
return errors.New("data is wrong length. should not happen")
5962
}
60-
copy(v.Data[:], data[:33])
63+
copy(v.TweakData[:], data[:TweakDataLength])
6164

6265
// we only try to read HighestValue if the data is there
63-
if len(data) == 33+8 {
64-
err := binary.Read(bytes.NewReader(data[33:]), binary.BigEndian, &v.HighestValue)
65-
if err != nil {
66-
common.ErrorLogger.Println(err)
67-
return err
68-
}
66+
// revoke: if the data is not there it seems like an implementation error. prior, where dust was an option it made sense
67+
err := binary.Read(bytes.NewReader(data[TweakDataLength:]), binary.BigEndian, &v.HighestValue)
68+
if err != nil {
69+
common.ErrorLogger.Println(err)
70+
return err
6971
}
7072

7173
return nil

src/common/types/tweak_interface.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package types
2+
3+
type TweakDusted interface {
4+
HighestValue() uint64
5+
Tweak() [33]byte // todo pointer to show none?
6+
}

src/common/types/tweakindex.go

+16-12
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import (
1111
// there is no transaction cut-through, so it will keep a full history.
1212
// The tweaks will most likely not be sorted in any meaningful way and have no metadata attached.
1313
type TweakIndex struct {
14-
BlockHash string `json:"block_hash"`
15-
BlockHeight uint32 `json:"block_height"`
16-
Data [][33]byte `json:"data"`
14+
BlockHash string `json:"block_hash"`
15+
BlockHeight uint32 `json:"block_height"`
16+
Data [][TweakDataLength]byte `json:"data"`
1717
}
1818

1919
func PairFactoryTweakIndex() Pair {
@@ -29,7 +29,7 @@ func (v *TweakIndex) SerialiseKey() ([]byte, error) {
2929
func (v *TweakIndex) SerialiseData() ([]byte, error) {
3030

3131
// todo can this be made more efficiently?
32-
totalLength := len(v.Data) * 33
32+
totalLength := len(v.Data) * TweakDataLength
3333
flattened := make([]byte, 0, totalLength)
3434

3535
for _, byteArray := range v.Data {
@@ -51,27 +51,31 @@ func (v *TweakIndex) DeSerialiseKey(key []byte) error {
5151
}
5252

5353
func (v *TweakIndex) DeSerialiseData(data []byte) error {
54-
if len(data)%33 != 0 {
54+
if len(data)%TweakDataLength != 0 {
5555
common.ErrorLogger.Printf("wrong data length: %+v", data)
5656
return errors.New("data is wrong length. should not happen")
5757
}
5858

59-
numArrays := len(data) / 33
60-
v.Data = make([][33]byte, numArrays)
59+
numArrays := len(data) / TweakDataLength
60+
v.Data = make([][TweakDataLength]byte, numArrays)
6161
// Iterate and copy segments from the flat slice into the new array of arrays
6262
for i := 0; i < numArrays; i++ {
63-
copy(v.Data[i][:], data[i*33:(i+1)*33])
63+
copy(v.Data[i][:], data[i*TweakDataLength:(i+1)*TweakDataLength])
6464
}
6565
return nil
6666
}
6767

6868
// TweakIndexFromTweakArray builds a TweakIndex from a slice of Tweak
6969
// comes without blockHash or height
70-
func TweakIndexFromTweakArray(tweaks []Tweak) *TweakIndex {
70+
func TweakIndexFromTweakArray(tweaksMap map[string]Tweak, block *Block) *TweakIndex {
71+
// todo benchmark the sorting, should not create too much overhead,
72+
// seems more like a nice to have for comparisons across implementations
7173
var index TweakIndex
7274
// can only panic hence no error output
73-
for _, tweak := range tweaks {
74-
index.Data = append(index.Data, tweak.Data)
75+
for _, tx := range block.Txs {
76+
if tweak, exists := tweaksMap[tx.Txid]; exists {
77+
index.Data = append(index.Data, tweak.TweakData)
78+
}
7579
}
7680
return &index
7781
}
@@ -84,7 +88,7 @@ func (v *TweakIndex) ToTweakArray() (tweaks []Tweak) {
8488
BlockHash: v.BlockHash,
8589
BlockHeight: v.BlockHeight,
8690
Txid: "",
87-
Data: data,
91+
TweakData: data,
8892
})
8993
}
9094
return

0 commit comments

Comments
 (0)