Skip to content

Commit ff7aeb1

Browse files
committed
add README and fix config method
1 parent 9e9af5a commit ff7aeb1

File tree

5 files changed

+75
-45
lines changed

5 files changed

+75
-45
lines changed

config/config.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -394,41 +394,3 @@ func ApplyShorterUpgradeRoundsForDevNetworks(id protocol.NetworkID) {
394394
}
395395
}
396396
}
397-
398-
// NormalizeVoteCompressionTableSize validates and normalizes the VoteCompressionDynamicTableSize config value.
399-
// Supported values are powers of 2 in the range [16, 1024].
400-
// Values >= 1024 clamp to 1024.
401-
// Values 1-15 are below the minimum and return 0 (disabled).
402-
// Values between supported powers of 2 round down to the nearest supported value.
403-
// Logs a message if the configured value is adjusted.
404-
// Returns the normalized size.
405-
func NormalizeVoteCompressionTableSize(configured uint, log logger) uint {
406-
if configured == 0 {
407-
return 0
408-
}
409-
if configured < 16 {
410-
log.Warnf("VoteCompressionDynamicTableSize configured as %d is invalid (minimum 16). Dynamic vote compression disabled.", configured)
411-
return 0
412-
}
413-
if configured >= 1024 {
414-
if configured != 1024 {
415-
log.Infof("VoteCompressionDynamicTableSize configured as %d, using nearest supported value: 1024", configured)
416-
}
417-
return 1024
418-
}
419-
420-
// Round down to nearest power of 2 within supported range [16, 512]
421-
supportedSizes := []uint{512, 256, 128, 64, 32, 16}
422-
for _, size := range supportedSizes {
423-
if configured >= size {
424-
if configured != size {
425-
log.Infof("VoteCompressionDynamicTableSize configured as %d, using nearest supported value: %d", configured, size)
426-
}
427-
return size
428-
}
429-
}
430-
431-
// Should never reach here given the checks above
432-
log.Warnf("VoteCompressionDynamicTableSize configured as %d is invalid. Dynamic vote compression disabled.", configured)
433-
return 0
434-
}

config/localTemplate.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,3 +1064,42 @@ func (cfg *Local) TracksCatchpoints() bool {
10641064
}
10651065
return false
10661066
}
1067+
1068+
// NormalizedVoteCompressionTableSize validates and normalizes the VoteCompressionDynamicTableSize config value.
1069+
// Supported values are powers of 2 in the range [16, 1024].
1070+
// Values >= 1024 clamp to 1024.
1071+
// Values 1-15 are below the minimum and return 0 (disabled).
1072+
// Values between supported powers of 2 round down to the nearest supported value.
1073+
// Logs a message if the configured value is adjusted.
1074+
// Returns the normalized size.
1075+
func (cfg Local) NormalizedVoteCompressionTableSize(log logger) uint {
1076+
configured := cfg.VoteCompressionDynamicTableSize
1077+
if configured == 0 {
1078+
return 0
1079+
}
1080+
if configured < 16 {
1081+
log.Warnf("VoteCompressionDynamicTableSize configured as %d is invalid (minimum 16). Dynamic vote compression disabled.", configured)
1082+
return 0
1083+
}
1084+
if configured >= 1024 {
1085+
if configured != 1024 {
1086+
log.Infof("VoteCompressionDynamicTableSize configured as %d, using nearest supported value: 1024", configured)
1087+
}
1088+
return 1024
1089+
}
1090+
1091+
// Round down to nearest power of 2 within supported range [16, 512]
1092+
supportedSizes := []uint{512, 256, 128, 64, 32, 16}
1093+
for _, size := range supportedSizes {
1094+
if configured >= size {
1095+
if configured != size {
1096+
log.Infof("VoteCompressionDynamicTableSize configured as %d, using nearest supported value: %d", configured, size)
1097+
}
1098+
return size
1099+
}
1100+
}
1101+
1102+
// Should never reach here given the checks above
1103+
log.Warnf("VoteCompressionDynamicTableSize configured as %d is invalid. Dynamic vote compression disabled.", configured)
1104+
return 0
1105+
}

network/p2pNetwork.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ func NewP2PNetwork(log logging.Logger, cfg config.Local, datadir string, phonebo
357357

358358
func (n *P2PNetwork) setup() error {
359359
// Validate and normalize vote compression table size
360-
n.voteCompressionDynamicTableSize = config.NormalizeVoteCompressionTableSize(n.config.VoteCompressionDynamicTableSize, n.log)
360+
n.voteCompressionDynamicTableSize = n.config.NormalizedVoteCompressionTableSize(n.log)
361361

362362
if n.broadcaster.slowWritingPeerMonitorInterval == 0 {
363363
n.broadcaster.slowWritingPeerMonitorInterval = slowWritingPeerMonitorInterval

network/vpack/README.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
# Stateless *vpack* wire format
1+
# *vpack* wire format
22

3-
This document specifies the byte‑level (on‑wire) layout produced by `StatelessEncoder.CompressVote` and accepted by `StatelessDecoder.DecompressVote`.
3+
This document specifies the byte‑level (on‑wire) layout for vote compression.
44
The goal is to minimize vote size while retaining a 1‑to‑1, loss‑free mapping to the canonical msgpack representation of `agreement.UnauthenticatedVote`.
55
The canonical msgpack representation we rely on is provided by agreement/msgp_gen.go, generated by our [custom msgpack code generator](https://github.com/algorand/msgp)
66
which ensures fields are generated in lexicographic order, omit empty key-value pairs, and use specific formats for certain types as defined in
77
[our specification](https://github.com/algorandfoundation/specs/blob/c0331123148971e4705f25b9c937cb23e5ee28d1/dev/crypto.md#L22-L40).
88

9+
## Compression Layers
10+
11+
Vote compression uses two layers:
12+
13+
1. **Stateless compression** (`StatelessEncoder`/`StatelessDecoder`): Removes msgpack formatting and field names, replacing them with a bitmask. This is always applied and has no memory overhead.
14+
15+
2. **Stateful compression (optional)** (`StatefulEncoder`/`StatefulDecoder`): Further compresses by replacing frequently repeated values with references to LRU tables and a sliding window. This layer requires per-connection state (configurable size, e.g., ~224KB per encoder/decoder pair for 1024-entry tables) and is optional. When used, it operates on the output of the stateless layer.
16+
17+
Both layers use the same 2-byte header format, with byte 0 used by the stateless layer and byte 1 used by the stateful layer (zero when stateful compression is not used).
18+
919
---
1020

1121
## 1. High‑level structure
@@ -26,10 +36,10 @@ No field names appear, only values.
2636

2737
| Offset | Description |
2838
| ------ | -------------------------------------------------------------- |
29-
| `0` | Presence flags for optional values (LSB first, see table). |
30-
| `1` | Reserved, currently zero. |
39+
| `0` | Presence flags for optional values (LSB first, see §2.1). |
40+
| `1` | Stateful compression flags (zero if not using stateful layer, see §2.2). |
3141

32-
### 2.1 Bit‑mask layout (byte 0)
42+
### 2.1 Stateless bit‑mask layout (byte 0)
3343

3444
| Bit | Flag | Field enabled | Encoded size |
3545
| --- | ----------- | -------------------------------- | ------------ |
@@ -48,6 +58,25 @@ Integers use msgpack's variable-length unsigned integer encoding:
4858
- `uint32` 5 bytes in length (marker byte 0xce + 4-byte value)
4959
- `uint64` 9 bytes in length (marker byte 0xcf + 8-byte value)
5060

61+
### 2.2 Stateful compression flags (byte 1)
62+
63+
When stateful compression is used, byte 1 encodes which values have been replaced by references:
64+
65+
| Bits | Field | Meaning |
66+
| ---- | ---------------- | ----------------------------------------------------------------- |
67+
| 0-1 | `rnd` encoding | `00`=literal, `01`=+1, `10`=-1, `11`=same as last round |
68+
| 2-4 | `prop` reference | `000`=literal, `001`-`111`=sliding window index (1-7) |
69+
| 5 | `snd` reference | `0`=literal (32 bytes), `1`=LRU table reference (2 bytes) |
70+
| 6 | `pk` reference | `0`=literal (96 bytes), `1`=LRU table reference (2 bytes) |
71+
| 7 | `pk2` reference | `0`=literal (96 bytes), `1`=LRU table reference (2 bytes) |
72+
73+
The stateful layer uses:
74+
- **Round delta encoding**: Most votes reference the same round or adjacent rounds, so 2 bits can encode common cases.
75+
- **Proposal sliding window**: A 7-entry HPACK-style window tracks recent proposal values. Usually all votes in a round vote for the same proposal.
76+
- **LRU tables**: Three 2-way set-associative hash tables cache recently seen values for `snd` (sender addresses), `pk` (public key + signature pairs), and `pk2` (second-tier key + signature pairs).
77+
78+
When stateful compression is not used, byte 1 must be zero.
79+
5180
---
5281

5382
## 3. Field serialization order

network/wsNetwork.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ func (wn *WebsocketNetwork) setup() error {
562562
wn.dialer = limitcaller.MakeRateLimitingDialer(wn.phonebook, preferredResolver)
563563

564564
// Validate and normalize vote compression table size
565-
wn.voteCompressionDynamicTableSize = config.NormalizeVoteCompressionTableSize(wn.config.VoteCompressionDynamicTableSize, wn.log)
565+
wn.voteCompressionDynamicTableSize = wn.config.NormalizedVoteCompressionTableSize(wn.log)
566566

567567
wn.upgrader.ReadBufferSize = 4096
568568
wn.upgrader.WriteBufferSize = 4096

0 commit comments

Comments
 (0)