@mblaney/holster 1.0.31 → 1.0.32
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.
- package/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/package.json +1 -1
- package/src/ham.js +47 -9
- package/src/utils.js +1 -14
package/package.json
CHANGED
package/src/ham.js
CHANGED
|
@@ -11,11 +11,10 @@ const Ham = (state, currentState, value, currentValue, signed = false) => {
|
|
|
11
11
|
|
|
12
12
|
if (state > currentState) return {incoming: true}
|
|
13
13
|
|
|
14
|
-
// state is equal to currentState
|
|
15
|
-
//
|
|
16
|
-
// owner can only create one value per timestamp.
|
|
14
|
+
// state is equal to currentState. Old data is expected to propagate through
|
|
15
|
+
// the network, but conflicting values with same signed timestamp are ignored
|
|
16
|
+
// since the owner can only create one value per timestamp.
|
|
17
17
|
if (signed && value !== currentValue) {
|
|
18
|
-
console.log(`Signed timestamp ${state}: reject conflicting value`)
|
|
19
18
|
return {historical: true}
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -96,15 +95,36 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
96
95
|
const signedTimestamps = new Set()
|
|
97
96
|
const signedProperties = new Set()
|
|
98
97
|
|
|
99
|
-
//
|
|
98
|
+
// Skip verification of timestamps that are older than what's in the graph
|
|
99
|
+
// to prevent old signatures from distributed messages causing conflicts
|
|
100
|
+
const incomingTimestamps = Object.entries(node._[">"] || {})
|
|
101
|
+
|
|
100
102
|
for (const [key, sig] of Object.entries(node._["s"] || {})) {
|
|
101
103
|
const asNumber = Number(key)
|
|
102
104
|
if (!isNaN(asNumber) && asNumber > 0) {
|
|
105
|
+
// Check if this timestamp is for properties that already have newer
|
|
106
|
+
// versions in graph.
|
|
107
|
+
const hasNewerInGraph = incomingTimestamps.some(([prop, ts]) => {
|
|
108
|
+
if (Number(ts) === asNumber) {
|
|
109
|
+
const graphState =
|
|
110
|
+
graph[soul] && graph[soul]._[">"] ? graph[soul]._[">"][prop] : 0
|
|
111
|
+
// Skip verification if graph has a newer timestamp for property.
|
|
112
|
+
if (graphState > asNumber) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return false
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (hasNewerInGraph) {
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
const isValid = await SEA.verifyTimestamp(asNumber, sig, pub)
|
|
104
124
|
if (isValid) {
|
|
105
125
|
signedTimestamps.add(asNumber)
|
|
106
126
|
// Add all properties with this timestamp to signed set
|
|
107
|
-
for (const [prop, ts] of
|
|
127
|
+
for (const [prop, ts] of incomingTimestamps) {
|
|
108
128
|
if (Number(ts) === asNumber) {
|
|
109
129
|
signedProperties.add(prop)
|
|
110
130
|
}
|
|
@@ -113,9 +133,27 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
113
133
|
}
|
|
114
134
|
}
|
|
115
135
|
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
// TODO: Remove per-property signature verification after old data has
|
|
137
|
+
// been cleared. Only check per-property signatures if there are
|
|
138
|
+
// properties without timestamp signatures.
|
|
139
|
+
const propertiesWithoutTimestampSigs = Object.keys(node).filter(k => {
|
|
140
|
+
if (k === "_" || k === utils.userPublicKey) return false
|
|
141
|
+
return !incomingTimestamps.some(
|
|
142
|
+
([prop, ts]) => prop === k && signedTimestamps.has(Number(ts)),
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
if (propertiesWithoutTimestampSigs.length > 0) {
|
|
147
|
+
const nodeToVerify = {_: node._}
|
|
148
|
+
for (const prop of propertiesWithoutTimestampSigs) {
|
|
149
|
+
nodeToVerify[prop] = node[prop]
|
|
150
|
+
}
|
|
151
|
+
const perPropertySignedProps = await SEA.verifyProperties(
|
|
152
|
+
nodeToVerify,
|
|
153
|
+
pub,
|
|
154
|
+
)
|
|
155
|
+
perPropertySignedProps.forEach(prop => signedProperties.add(prop))
|
|
156
|
+
}
|
|
119
157
|
|
|
120
158
|
// If no properties verified, skip this update
|
|
121
159
|
if (signedProperties.size === 0) {
|
package/src/utils.js
CHANGED
|
@@ -89,21 +89,8 @@ export const graph = (soul, data, sig, pub, timestamp) => {
|
|
|
89
89
|
g[soul]._[">"][key] = timestamp || Date.now()
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
// Store signatures in node._["s"]
|
|
93
|
-
// sig can be:
|
|
94
|
-
// - object: per-property signatures (legacy format)
|
|
95
|
-
// - string: timestamp signature (new format)
|
|
96
92
|
if (sig && pub && timestamp) {
|
|
97
|
-
g[soul]._["s"] = {}
|
|
98
|
-
if (typeof sig === "string") {
|
|
99
|
-
// Timestamp signature: store with timestamp as key
|
|
100
|
-
g[soul]._["s"][timestamp] = sig
|
|
101
|
-
} else if (typeof sig === "object") {
|
|
102
|
-
// Per-property signatures: store with property names as keys
|
|
103
|
-
for (const [key, signature] of Object.entries(sig)) {
|
|
104
|
-
g[soul]._["s"][key] = signature
|
|
105
|
-
}
|
|
106
|
-
}
|
|
93
|
+
g[soul]._["s"] = {[timestamp]: sig}
|
|
107
94
|
g[soul][userPublicKey] = pub
|
|
108
95
|
g[soul]._[">"][userPublicKey] = timestamp
|
|
109
96
|
}
|