Skip to content

Commit 234409f

Browse files
committed
Added a v1 <> v2 compare test
1 parent 9e11f1e commit 234409f

File tree

6 files changed

+301
-0
lines changed

6 files changed

+301
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ benchmark-v2: build-benchmark
1414
@echo "Running v2 gRPC benchmark only..."
1515
./bin/benchmark -v1=false -v2 $(ARGS)
1616

17+
.PHONY: compare
18+
compare: build-benchmark
19+
@echo "Comparing v1 and v2 data..."
20+
./bin/benchmark -compare $(ARGS)
21+
1722
.PHONY: build-benchmark
1823
build-benchmark:
1924
@echo "Building benchmark tool..."
@@ -24,3 +29,4 @@ build-benchmark:
2429
# make benchmark ARGS="-startheight=100 -endheight=200"
2530
# make benchmark-v1 ARGS="-startheight=100 -endheight=200"
2631
# make benchmark-v2 ARGS="-startheight=100 -endheight=200"
32+
# make compare ARGS="-startheight=100 -endheight=200"

cmd/benchmark/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ make build-benchmark
2525
./bin/benchmark -v1=false -v2 -startheight=100 -endheight=200
2626
```
2727

28+
### Compare v1 and v2 data (validation)
29+
```bash
30+
./bin/benchmark -compare -startheight=100 -endheight=200
31+
```
32+
2833
### Using Makefile
2934
```bash
3035
# Run both
@@ -35,6 +40,9 @@ make benchmark-v1 ARGS="-startheight=100 -endheight=200"
3540

3641
# Run only v2
3742
make benchmark-v2 ARGS="-startheight=100 -endheight=200"
43+
44+
# Compare data
45+
make benchmark ARGS="-compare -startheight=264100 -endheight=264200"
3846
```
3947

4048
## Command Line Flags
@@ -45,6 +53,7 @@ make benchmark-v2 ARGS="-startheight=100 -endheight=200"
4553
- `-grpc`: gRPC server host:port (default: "127.0.0.1:50051")
4654
- `-v1`: Run v1 HTTP benchmark (default: true)
4755
- `-v2`: Run v2 gRPC benchmark (default: true)
56+
- `-compare`: Compare v1 and v2 data instead of benchmarking (default: false)
4857

4958
## What it measures
5059

cmd/benchmark/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,23 @@ func main() {
1616
grpcHost = flag.String("grpc", "127.0.0.1:50051", "gRPC server host:port")
1717
runV1 = flag.Bool("v1", true, "Run v1 HTTP benchmark")
1818
runV2 = flag.Bool("v2", true, "Run v2 gRPC benchmark")
19+
compare = flag.Bool("compare", false, "Compare v1 and v2 data instead of benchmarking")
1920
)
2021
flag.Parse()
2122

2223
// Setup logging
2324
logging.SetLogLevel(zerolog.InfoLevel)
2425

26+
if *compare {
27+
logging.L.Info().
28+
Uint64("start_height", *startHeight).
29+
Uint64("end_height", *endHeight).
30+
Msg("Starting data comparison")
31+
32+
benchmark.CompareV1V2Results(*startHeight, *endHeight, *httpURL, *grpcHost)
33+
return
34+
}
35+
2536
logging.L.Info().
2637
Uint64("start_height", *startHeight).
2738
Uint64("end_height", *endHeight).

internal/benchmark/compare.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package benchmark
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/setavenger/blindbit-lib/logging"
9+
"github.com/setavenger/blindbit-lib/networking"
10+
"github.com/setavenger/blindbit-lib/proto/pb"
11+
"google.golang.org/grpc"
12+
"google.golang.org/grpc/credentials/insecure"
13+
)
14+
15+
// CompareV1V2Results compares v1 and v2 results for validation
16+
func CompareV1V2Results(startHeight, endHeight uint64, httpURL, grpcHost string) {
17+
logging.L.Info().Msgf("Comparing v1 and v2 results from height %d to %d", startHeight, endHeight)
18+
19+
for height := startHeight; height <= endHeight; height++ {
20+
logging.L.Info().Uint64("height", height).Msg("comparing block")
21+
22+
// Fetch v1 data
23+
v1Data, err := fetchBlockDataV1(height, &networking.ClientBlindBit{BaseURL: httpURL})
24+
if err != nil {
25+
logging.L.Err(err).Uint64("height", height).Msg("failed to fetch v1 data")
26+
continue
27+
}
28+
29+
// Fetch v2 data
30+
v2Data, err := fetchBlockDataV2(height, grpcHost)
31+
if err != nil {
32+
logging.L.Err(err).Uint64("height", height).Msg("failed to fetch v2 data")
33+
continue
34+
}
35+
36+
// Quick validation
37+
if len(v1Data.Tweaks) != len(v2Data.Tweaks) {
38+
logging.L.Warn().Uint64("height", height).
39+
Int("v1_tweaks", len(v1Data.Tweaks)).
40+
Int("v2_tweaks", len(v2Data.Tweaks)).
41+
Msg("tweak count mismatch")
42+
}
43+
44+
if v1Data.FilterNew != nil && v2Data.FilterNew != nil {
45+
if len(v1Data.FilterNew.Data) != len(v2Data.FilterNew.Data) {
46+
logging.L.Warn().Uint64("height", height).
47+
Int("v1_filter_new", len(v1Data.FilterNew.Data)).
48+
Int("v2_filter_new", len(v2Data.FilterNew.Data)).
49+
Msg("new UTXOs filter data length mismatch")
50+
}
51+
}
52+
53+
if v1Data.FilterSpent != nil && v2Data.FilterSpent != nil {
54+
if len(v1Data.FilterSpent.Data) != len(v2Data.FilterSpent.Data) {
55+
logging.L.Warn().Uint64("height", height).
56+
Int("v1_filter_spent", len(v1Data.FilterSpent.Data)).
57+
Int("v2_filter_spent", len(v2Data.FilterSpent.Data)).
58+
Msg("spent outpoints filter data length mismatch")
59+
}
60+
}
61+
62+
logging.L.Info().Uint64("height", height).Msg("block comparison completed")
63+
}
64+
65+
logging.L.Info().Msg("All block comparisons completed")
66+
}
67+
68+
// fetchBlockDataV2 fetches block data using v2 gRPC API
69+
func fetchBlockDataV2(height uint64, grpcHost string) (*BlockDataV2, error) {
70+
// Connect to gRPC server
71+
conn, err := grpc.Dial(grpcHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
72+
if err != nil {
73+
return nil, fmt.Errorf("failed to connect to gRPC server: %v", err)
74+
}
75+
defer conn.Close()
76+
77+
client := pb.NewOracleServiceClient(conn)
78+
79+
// Use streaming API to fetch single block
80+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
81+
defer cancel()
82+
83+
req := &pb.RangedBlockHeightRequest{
84+
Start: height,
85+
End: height,
86+
}
87+
88+
stream, err := client.StreamBlockBatchSlim(ctx, req)
89+
if err != nil {
90+
return nil, fmt.Errorf("failed to start streaming: %v", err)
91+
}
92+
93+
// Receive the single batch
94+
batch, err := stream.Recv()
95+
if err != nil {
96+
return nil, fmt.Errorf("failed to receive batch: %v", err)
97+
}
98+
99+
// Convert protobuf data to our format
100+
return &BlockDataV2{
101+
Height: height,
102+
FilterNew: convertFilterData(batch.NewUtxosFilter, batch.BlockIdentifier),
103+
FilterSpent: convertFilterData(batch.SpentUtxosFilter, batch.BlockIdentifier),
104+
Tweaks: convertTweaksToArray(batch.Tweaks),
105+
}, nil
106+
}
107+
108+
// convertTweaksToArray converts [][]byte to [][33]byte
109+
func convertTweaksToArray(tweaks [][]byte) [][33]byte {
110+
result := make([][33]byte, len(tweaks))
111+
for i, tweak := range tweaks {
112+
if len(tweak) == 33 {
113+
copy(result[i][:], tweak)
114+
}
115+
}
116+
return result
117+
}
118+
119+
// convertFilterData converts protobuf FilterData to networking.Filter
120+
func convertFilterData(pbFilter *pb.FilterData, blockIdentifier *pb.BlockIdentifier) *networking.Filter {
121+
if pbFilter == nil {
122+
return nil
123+
}
124+
125+
// Convert block hash from bytes to [32]byte
126+
var blockHash [32]byte
127+
if len(blockIdentifier.BlockHash) == 32 {
128+
copy(blockHash[:], blockIdentifier.BlockHash)
129+
}
130+
131+
return &networking.Filter{
132+
FilterType: uint8(pbFilter.FilterType),
133+
BlockHeight: blockIdentifier.BlockHeight,
134+
BlockHash: blockHash,
135+
Data: pbFilter.Data,
136+
}
137+
}
138+
139+
// BlockDataV2 represents the block data structure for v2
140+
type BlockDataV2 struct {
141+
Height uint64
142+
FilterNew *networking.Filter
143+
FilterSpent *networking.Filter
144+
Tweaks [][33]byte
145+
}

internal/benchmark/compare_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package benchmark
2+
3+
import (
4+
"testing"
5+
6+
"github.com/rs/zerolog"
7+
"github.com/setavenger/blindbit-lib/logging"
8+
"github.com/setavenger/blindbit-lib/networking"
9+
)
10+
11+
// TestCompareV1V2Results compares results from v1 HTTP and v2 gRPC endpoints
12+
func TestCompareV1V2Results(t *testing.T) {
13+
// Configuration
14+
httpURL := "http://127.0.0.1:8000"
15+
grpcHost := "127.0.0.1:50051"
16+
testHeight := uint64(100) // Adjust to a height you know has data
17+
18+
// Setup logging
19+
logging.SetLogLevel(zerolog.InfoLevel)
20+
21+
t.Logf("Comparing v1 and v2 results for block height %d", testHeight)
22+
23+
// Fetch data from v1 (HTTP)
24+
v1Data, err := fetchBlockDataV1(testHeight, &networking.ClientBlindBit{BaseURL: httpURL})
25+
if err != nil {
26+
t.Fatalf("Failed to fetch v1 data: %v", err)
27+
}
28+
29+
// Fetch data from v2 (gRPC)
30+
v2Data, err := fetchBlockDataV2(testHeight, grpcHost)
31+
if err != nil {
32+
t.Fatalf("Failed to fetch v2 data: %v", err)
33+
}
34+
35+
// Compare results
36+
t.Run("CompareTweaks", func(t *testing.T) {
37+
compareTweaks(t, v1Data.Tweaks, v2Data.Tweaks)
38+
})
39+
40+
t.Run("CompareNewUTXOsFilter", func(t *testing.T) {
41+
compareFilter(t, "NewUTXOs", v1Data.FilterNew, v2Data.FilterNew)
42+
})
43+
44+
t.Run("CompareSpentOutpointsFilter", func(t *testing.T) {
45+
compareFilter(t, "SpentOutpoints", v1Data.FilterSpent, v2Data.FilterSpent)
46+
})
47+
48+
t.Log("All comparisons passed - v1 and v2 endpoints return identical data")
49+
}
50+
51+
// compareTweaks compares tweak arrays from v1 and v2
52+
func compareTweaks(t *testing.T, v1Tweaks, v2Tweaks [][33]byte) {
53+
if len(v1Tweaks) != len(v2Tweaks) {
54+
t.Errorf("Tweak count mismatch: v1=%d, v2=%d", len(v1Tweaks), len(v2Tweaks))
55+
return
56+
}
57+
58+
t.Logf("Comparing %d tweaks", len(v1Tweaks))
59+
60+
for i, v1Tweak := range v1Tweaks {
61+
if i >= len(v2Tweaks) {
62+
t.Errorf("v2 tweaks array too short at index %d", i)
63+
continue
64+
}
65+
66+
v2Tweak := v2Tweaks[i]
67+
if v1Tweak != v2Tweak {
68+
t.Errorf("Tweak mismatch at index %d: v1=%x, v2=%x",
69+
i, v1Tweak, v2Tweak)
70+
}
71+
}
72+
}
73+
74+
// compareFilter compares filter data from v1 and v2
75+
func compareFilter(t *testing.T, filterName string, v1Filter, v2Filter *networking.Filter) {
76+
if v1Filter == nil && v2Filter == nil {
77+
t.Logf("%s filter: both nil (no data)", filterName)
78+
return
79+
}
80+
81+
if v1Filter == nil {
82+
t.Errorf("%s filter: v1 is nil but v2 is not", filterName)
83+
return
84+
}
85+
86+
if v2Filter == nil {
87+
t.Errorf("%s filter: v2 is nil but v1 is not", filterName)
88+
return
89+
}
90+
91+
t.Logf("%s filter: comparing data", filterName)
92+
93+
// Compare filter data
94+
if len(v1Filter.Data) != len(v2Filter.Data) {
95+
t.Errorf("%s filter data length mismatch: v1=%d, v2=%d",
96+
filterName, len(v1Filter.Data), len(v2Filter.Data))
97+
return
98+
}
99+
100+
for i, v1Byte := range v1Filter.Data {
101+
if i >= len(v2Filter.Data) {
102+
t.Errorf("%s filter: v2 data too short at index %d", filterName, i)
103+
continue
104+
}
105+
106+
v2Byte := v2Filter.Data[i]
107+
if v1Byte != v2Byte {
108+
t.Errorf("%s filter data mismatch at index %d: v1=%x, v2=%x",
109+
filterName, i, v1Byte, v2Byte)
110+
}
111+
}
112+
113+
// Compare block hash
114+
if v1Filter.BlockHash != v2Filter.BlockHash {
115+
t.Errorf("%s filter block hash mismatch: v1=%x, v2=%x",
116+
filterName, v1Filter.BlockHash, v2Filter.BlockHash)
117+
}
118+
119+
// Compare block height
120+
if v1Filter.BlockHeight != v2Filter.BlockHeight {
121+
t.Errorf("%s filter block height mismatch: v1=%d, v2=%d",
122+
filterName, v1Filter.BlockHeight, v2Filter.BlockHeight)
123+
}
124+
}

internal/server/v2/service.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,12 @@ func (s *OracleService) StreamBlockBatchFull(
328328
req *pb.RangedBlockHeightRequest, stream pb.OracleService_StreamBlockBatchFullServer,
329329
) error {
330330
for height := req.Start; height <= req.End; height++ {
331+
select {
332+
case <-stream.Context().Done():
333+
logging.L.Debug().Msg("stream context cancelled")
334+
return nil
335+
default:
336+
}
331337
headerInv, err := dblevel.FetchByBlockHeightBlockHeaderInv(uint32(height))
332338
if err != nil {
333339
logging.L.Err(err).Msg("error fetching block header inv")

0 commit comments

Comments
 (0)