@peerbit/shared-log 3.1.9 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,11 +3,25 @@ import {
3
3
  deserialize,
4
4
  serialize,
5
5
  field,
6
- BorshError
6
+ option,
7
+ BinaryReader
7
8
  } from "@dao-xyz/borsh";
9
+ import { TransportMessage } from "./message.js";
10
+ import { Observer, Replicator, Role } from "./role.js";
11
+ import { PublicSignKey } from "@peerbit/crypto";
12
+ import yallist from "yallist";
13
+
14
+ export type ReplicationLimits = { min: MinReplicas; max?: MinReplicas };
15
+
16
+ export type ReplicatorRect = {
17
+ publicKey: PublicSignKey;
18
+ offset: number;
19
+ role: Replicator;
20
+ };
8
21
 
9
22
  interface SharedLog {
10
- getReplicatorsSorted(): { hash: string; timestamp: number }[] | undefined;
23
+ replicas: Partial<ReplicationLimits>;
24
+ getReplicatorsSorted(): yallist<ReplicatorRect> | undefined;
11
25
  }
12
26
 
13
27
  export class MinReplicas {
@@ -30,6 +44,25 @@ export class AbsoluteReplicas extends MinReplicas {
30
44
  }
31
45
  }
32
46
 
47
+ @variant([1, 0])
48
+ export class RequestRoleMessage extends TransportMessage {
49
+ constructor() {
50
+ super();
51
+ }
52
+ }
53
+
54
+ @variant([1, 1])
55
+ export class ResponseRoleMessage extends TransportMessage {
56
+ @field({ type: option(Role) })
57
+ role: Observer | Replicator;
58
+
59
+ constructor(role: Observer | Replicator) {
60
+ super();
61
+
62
+ this.role = role;
63
+ }
64
+ }
65
+
33
66
  /*
34
67
  @variant(1)
35
68
  export class RelativeMinReplicas extends MinReplicas {
@@ -65,11 +98,23 @@ export const decodeReplicas = (entry: {
65
98
 
66
99
  export const maxReplicas = (
67
100
  log: SharedLog,
68
- entries: { meta: { data?: Uint8Array } }[]
101
+ entries:
102
+ | { meta: { data?: Uint8Array } }[]
103
+ | IterableIterator<{ meta: { data?: Uint8Array } }>
69
104
  ) => {
70
105
  let max = 0;
71
106
  for (const entry of entries) {
72
107
  max = Math.max(decodeReplicas(entry).getValue(log), max);
73
108
  }
74
- return max;
109
+ const lower = log.replicas.min?.getValue(log) || 1;
110
+ const higher = log.replicas.max?.getValue(log) ?? Number.MAX_SAFE_INTEGER;
111
+ const numberOfLeaders = Math.max(Math.min(higher, max), lower);
112
+ return numberOfLeaders;
113
+ };
114
+
115
+ export const hashToUniformNumber = (hash: Uint8Array) => {
116
+ const seedNumber = new BinaryReader(
117
+ hash.subarray(hash.length - 4, hash.length)
118
+ ).u32();
119
+ return seedNumber / 0xffffffff; // bounded between 0 and 1
75
120
  };
package/src/role.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { field, variant } from "@dao-xyz/borsh";
1
+ import { field, option, variant, vec } from "@dao-xyz/borsh";
2
2
 
3
3
  export abstract class Role {}
4
4
 
@@ -8,19 +8,80 @@ export const NO_TYPE_VARIANT = new Uint8Array([0]);
8
8
  export class NoType extends Role {}
9
9
 
10
10
  export const OBSERVER_TYPE_VARIANT = new Uint8Array([1]);
11
+
11
12
  @variant(1)
12
13
  export class Observer extends Role {}
13
14
 
14
15
  export const REPLICATOR_TYPE_VARIANT = new Uint8Array([2]);
15
16
 
17
+ class ReplicationSegment {
18
+ @field({ type: "u64" })
19
+ timestamp: bigint;
20
+
21
+ @field({ type: "u32" })
22
+ private factorNominator: number;
23
+
24
+ @field({ type: option("u32") })
25
+ private offsetNominator?: number;
26
+
27
+ constructor(properties: {
28
+ factor: number;
29
+ timestamp?: bigint;
30
+ offset?: number;
31
+ }) {
32
+ const { factor, timestamp, offset } = properties;
33
+ if (factor > 1 || factor < 0) {
34
+ throw new Error("Expecting factor to be between 0 and 1, got: " + factor);
35
+ }
36
+
37
+ this.timestamp = timestamp ?? BigInt(+new Date());
38
+ this.factorNominator = Math.round(4294967295 * factor);
39
+
40
+ if (offset != null) {
41
+ if (offset > 1 || offset < 0) {
42
+ throw new Error(
43
+ "Expecting offset to be between 0 and 1, got: " + offset
44
+ );
45
+ }
46
+ this.offsetNominator = Math.round(4294967295 * offset);
47
+ }
48
+ }
49
+
50
+ get factor(): number {
51
+ return this.factorNominator / 4294967295;
52
+ }
53
+
54
+ get offset(): number | undefined {
55
+ return this.offsetNominator != null
56
+ ? this.offsetNominator / 4294967295
57
+ : undefined;
58
+ }
59
+ }
60
+
16
61
  @variant(2)
17
62
  export class Replicator extends Role {
18
- @field({ type: "u32" })
19
- multiplier: number; // 1 means I do the same amount of work as anyone else, 2 means double
63
+ @field({ type: vec(ReplicationSegment) })
64
+ segments: ReplicationSegment[];
20
65
 
21
- constructor() {
22
- // multiplier is unsupported for now, so contructor is empty
66
+ constructor(properties: {
67
+ factor: number;
68
+ timestamp?: bigint;
69
+ offset?: number;
70
+ }) {
23
71
  super();
24
- this.multiplier = 1;
72
+ const segment: ReplicationSegment = new ReplicationSegment(properties);
73
+ this.segments = [segment];
74
+ }
75
+
76
+ get factor(): number {
77
+ return this.segments[0]!.factor;
78
+ }
79
+
80
+ get offset(): number | undefined {
81
+ return this.segments[0]!.offset;
82
+ }
83
+
84
+ get timestamp(): bigint {
85
+ return this.segments[0]!.timestamp;
25
86
  }
26
87
  }