@xyo-network/boundwitness-builder 2.89.2 → 2.90.0
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/dist/browser/Builder.d.cts +35 -15
- package/dist/browser/Builder.d.cts.map +1 -1
- package/dist/browser/Builder.d.mts +35 -15
- package/dist/browser/Builder.d.mts.map +1 -1
- package/dist/browser/Builder.d.ts +35 -15
- package/dist/browser/Builder.d.ts.map +1 -1
- package/dist/browser/index.cjs +78 -44
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +78 -44
- package/dist/browser/index.js.map +1 -1
- package/dist/node/Builder.d.cts +35 -15
- package/dist/node/Builder.d.cts.map +1 -1
- package/dist/node/Builder.d.mts +35 -15
- package/dist/node/Builder.d.mts.map +1 -1
- package/dist/node/Builder.d.ts +35 -15
- package/dist/node/Builder.d.ts.map +1 -1
- package/dist/node/index.cjs +79 -45
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +79 -45
- package/dist/node/index.js.map +1 -1
- package/package.json +10 -10
- package/src/Builder.ts +119 -61
package/src/Builder.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toArrayBuffer, toUint8Array } from '@xylabs/arraybuffer'
|
|
2
2
|
import { assertEx } from '@xylabs/assert'
|
|
3
3
|
import { Address, Hash, hexFromArrayBuffer } from '@xylabs/hex'
|
|
4
|
-
import { JsonObject } from '@xylabs/object'
|
|
4
|
+
import { AnyObject, JsonObject } from '@xylabs/object'
|
|
5
5
|
import { AccountInstance } from '@xyo-network/account-model'
|
|
6
6
|
import { BoundWitness, BoundWitnessSchema } from '@xyo-network/boundwitness-model'
|
|
7
7
|
import { sortFields } from '@xyo-network/hash'
|
|
@@ -9,22 +9,22 @@ import { PayloadBuilder, PayloadBuilderBase, PayloadBuilderOptions, PayloadWrapp
|
|
|
9
9
|
import { ModuleError, Payload, Schema, WithMeta } from '@xyo-network/payload-model'
|
|
10
10
|
import { Mutex } from 'async-mutex'
|
|
11
11
|
|
|
12
|
-
type GeneratedBoundWitnessFields = 'addresses' | 'payload_hashes' | 'payload_schemas' | 'previous_hashes'
|
|
12
|
+
export type GeneratedBoundWitnessFields = 'addresses' | 'payload_hashes' | 'payload_schemas' | 'previous_hashes'
|
|
13
13
|
|
|
14
|
-
export interface BoundWitnessBuilderOptions<
|
|
15
|
-
extends Omit<PayloadBuilderOptions<Omit<
|
|
14
|
+
export interface BoundWitnessBuilderOptions<TBoundWitness extends BoundWitness = BoundWitness, TPayload extends Payload = Payload>
|
|
15
|
+
extends Omit<PayloadBuilderOptions<Omit<TBoundWitness, GeneratedBoundWitnessFields>>, 'schema'> {
|
|
16
16
|
readonly accounts?: AccountInstance[]
|
|
17
17
|
readonly destination?: string[]
|
|
18
|
-
readonly payloadHashes?:
|
|
19
|
-
readonly payloadSchemas?:
|
|
18
|
+
readonly payloadHashes?: TBoundWitness['payload_hashes']
|
|
19
|
+
readonly payloadSchemas?: TBoundWitness['payload_schemas']
|
|
20
20
|
readonly payloads?: TPayload[]
|
|
21
21
|
readonly sourceQuery?: Hash
|
|
22
|
-
readonly timestamp?:
|
|
22
|
+
readonly timestamp?: number
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export class BoundWitnessBuilder<
|
|
26
|
-
Omit<
|
|
27
|
-
BoundWitnessBuilderOptions<
|
|
25
|
+
export class BoundWitnessBuilder<TBoundWitness extends BoundWitness = BoundWitness, TPayload extends Payload = Payload> extends PayloadBuilderBase<
|
|
26
|
+
Omit<TBoundWitness, GeneratedBoundWitnessFields>,
|
|
27
|
+
BoundWitnessBuilderOptions<TBoundWitness> & { schema: BoundWitnessSchema }
|
|
28
28
|
> {
|
|
29
29
|
private static readonly _buildMutex = new Mutex()
|
|
30
30
|
private _accounts: AccountInstance[]
|
|
@@ -37,7 +37,7 @@ export class BoundWitnessBuilder<T extends BoundWitness = BoundWitness, TPayload
|
|
|
37
37
|
private _sourceQuery?: Hash
|
|
38
38
|
private _timestamp: boolean | number
|
|
39
39
|
|
|
40
|
-
constructor(options?: BoundWitnessBuilderOptions<
|
|
40
|
+
constructor(options?: BoundWitnessBuilderOptions<TBoundWitness, TPayload>) {
|
|
41
41
|
super({ ...options, schema: BoundWitnessSchema })
|
|
42
42
|
const { accounts, payloadHashes, payloadSchemas, payloads, sourceQuery, timestamp, destination } = options ?? {}
|
|
43
43
|
this._accounts = accounts ?? []
|
|
@@ -49,7 +49,11 @@ export class BoundWitnessBuilder<T extends BoundWitness = BoundWitness, TPayload
|
|
|
49
49
|
this._timestamp = timestamp ?? true
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
protected get addresses(): Address[] {
|
|
53
|
+
return this._accounts.map((account) => account.address)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected get payloadSchemas(): string[] {
|
|
53
57
|
return (
|
|
54
58
|
this._payloadSchemas ??
|
|
55
59
|
this._payloads.map((payload) => {
|
|
@@ -58,6 +62,18 @@ export class BoundWitnessBuilder<T extends BoundWitness = BoundWitness, TPayload
|
|
|
58
62
|
)
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
protected get previousHashBuffers(): (ArrayBuffer | null)[] {
|
|
66
|
+
return this._accounts.map((account) => account.previousHashBytes ?? null)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected get previousHashes(): (Hash | null)[] {
|
|
70
|
+
return this._accounts.map((account) => account.previousHash ?? null)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected get timestamp(): number {
|
|
74
|
+
return (this._timestamp = typeof this._timestamp === 'number' ? this._timestamp : Date.now())
|
|
75
|
+
}
|
|
76
|
+
|
|
61
77
|
static addressIndex<T extends BoundWitness>(payload: T, address: Address) {
|
|
62
78
|
const index = payload.addresses.indexOf(address)
|
|
63
79
|
if (index === -1) {
|
|
@@ -66,69 +82,107 @@ export class BoundWitnessBuilder<T extends BoundWitness = BoundWitness, TPayload
|
|
|
66
82
|
return index
|
|
67
83
|
}
|
|
68
84
|
|
|
69
|
-
static
|
|
70
|
-
return
|
|
85
|
+
static async build<TBoundWitness extends BoundWitness>(options: BoundWitnessBuilderOptions<TBoundWitness>) {
|
|
86
|
+
return await new BoundWitnessBuilder(options).build()
|
|
71
87
|
}
|
|
72
88
|
|
|
73
|
-
static
|
|
74
|
-
|
|
89
|
+
static override async dataHashableFields<T extends Payload = Payload<AnyObject>>(
|
|
90
|
+
schema: string,
|
|
91
|
+
fields?: Omit<T, 'schema' | '$hash' | '$meta'>,
|
|
92
|
+
): Promise<Omit<T, '$hash' | '$meta'>> {
|
|
93
|
+
return await PayloadBuilderBase.dataHashableFields(schema, fields)
|
|
75
94
|
}
|
|
76
95
|
|
|
77
|
-
async
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
static override async hashableFields<T extends Payload = Payload<AnyObject>>(
|
|
97
|
+
schema: string,
|
|
98
|
+
fields?: Omit<T, 'schema' | '$hash' | '$meta'>,
|
|
99
|
+
$meta?: JsonObject,
|
|
100
|
+
$hash?: Hash,
|
|
101
|
+
timestamp?: number,
|
|
102
|
+
): Promise<WithMeta<T>> {
|
|
103
|
+
return await PayloadBuilderBase.hashableFields(schema, fields, $meta, $hash, timestamp)
|
|
104
|
+
}
|
|
81
105
|
|
|
82
|
-
|
|
83
|
-
|
|
106
|
+
static previousHash<T extends BoundWitness>(boundWitness: T, address: Address) {
|
|
107
|
+
return boundWitness.previous_hashes[this.addressIndex(boundWitness, address)]
|
|
108
|
+
}
|
|
84
109
|
|
|
85
|
-
|
|
110
|
+
protected static async linkingFields<T extends BoundWitness = BoundWitness>(
|
|
111
|
+
accounts: AccountInstance[],
|
|
112
|
+
payloads?: Payload[],
|
|
113
|
+
timestamp = Date.now(),
|
|
114
|
+
) {
|
|
115
|
+
const addresses = accounts.map((account) => hexFromArrayBuffer(account.addressBytes, { prefix: false }))
|
|
116
|
+
const previous_hashes = accounts.map((account) => account.previousHash ?? null)
|
|
117
|
+
const payload_hashes = payloads ? await PayloadBuilder.dataHashes(payloads) : []
|
|
118
|
+
const payload_schemas = payloads?.map(({ schema }) => schema)
|
|
119
|
+
return { addresses, payload_hashes, payload_schemas, previous_hashes, timestamp } as Omit<T, '$meta' | '$hash' | 'schema'>
|
|
120
|
+
}
|
|
86
121
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
122
|
+
protected static async metaFields(
|
|
123
|
+
dataHash: Hash,
|
|
124
|
+
otherMeta?: JsonObject,
|
|
125
|
+
accounts?: AccountInstance[],
|
|
126
|
+
previousHashes?: (Hash | null)[],
|
|
127
|
+
destination?: Address[],
|
|
128
|
+
sourceQuery?: Hash,
|
|
129
|
+
): Promise<JsonObject> {
|
|
130
|
+
const meta: JsonObject = { ...otherMeta }
|
|
131
|
+
|
|
132
|
+
if (accounts?.length && previousHashes?.length) {
|
|
133
|
+
assertEx(accounts.length === previousHashes.length, 'accounts and previousHashes must have same length')
|
|
134
|
+
meta.signatures = await this.signatures(accounts, dataHash, previousHashes)
|
|
135
|
+
}
|
|
91
136
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
137
|
+
if (sourceQuery) {
|
|
138
|
+
meta.sourceQuery = sourceQuery
|
|
139
|
+
}
|
|
96
140
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
141
|
+
if (destination) {
|
|
142
|
+
meta.destination = destination
|
|
143
|
+
}
|
|
101
144
|
|
|
102
|
-
|
|
103
|
-
...hashableFields,
|
|
104
|
-
$hash: hash,
|
|
105
|
-
...metaHolder,
|
|
106
|
-
} as WithMeta<T>
|
|
107
|
-
return [ret, this._payloads, this._errors]
|
|
108
|
-
})
|
|
145
|
+
return meta
|
|
109
146
|
}
|
|
110
147
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const payload_hashes = assertEx(await this.getPayloadHashes(), 'Missing payload_hashes')
|
|
115
|
-
const payload_schemas = assertEx(this._payload_schemas, 'Missing payload_schemas')
|
|
116
|
-
const fields = { addresses, payload_hashes, payload_schemas, previous_hashes } as Omit<T, '$meta' | '$hash' | 'schema'>
|
|
117
|
-
const result = await BoundWitnessBuilder.dataHashableFields<T>(this._schema, fields)
|
|
148
|
+
protected static signature<T extends BoundWitness>(payload: T, address: Address) {
|
|
149
|
+
return payload.$meta.signatures[this.addressIndex(payload, address)]
|
|
150
|
+
}
|
|
118
151
|
|
|
119
|
-
|
|
152
|
+
protected static async signatures(accounts: AccountInstance[], hash: Hash, previousHashes: (Hash | ArrayBuffer | null)[]): Promise<string[]> {
|
|
153
|
+
const hashBytes = toArrayBuffer(hash)
|
|
154
|
+
const previousHashesBytes = previousHashes?.map((ph) => (ph ? toUint8Array(ph) : undefined))
|
|
155
|
+
return await Promise.all(accounts.map(async (account, index) => hexFromArrayBuffer(await account.sign(hashBytes, previousHashesBytes[index]))))
|
|
156
|
+
}
|
|
120
157
|
|
|
121
|
-
|
|
158
|
+
private static validateLinkingFields(bw: Pick<BoundWitness, 'payload_hashes' | 'payload_schemas'>) {
|
|
159
|
+
assertEx(bw.payload_hashes?.length === bw.payload_schemas?.length, 'Payload hash/schema mismatch')
|
|
160
|
+
assertEx(!bw.payload_hashes.some((hash) => !hash), () => 'nulls found in hashes')
|
|
161
|
+
assertEx(!bw.payload_schemas.some((schema) => !schema), 'nulls found in schemas')
|
|
162
|
+
}
|
|
122
163
|
|
|
123
|
-
|
|
164
|
+
async build(): Promise<[WithMeta<TBoundWitness>, TPayload[], ModuleError[]]> {
|
|
165
|
+
return await BoundWitnessBuilder._buildMutex.runExclusive(async () => {
|
|
166
|
+
const dataHashableFields = (await this.dataHashableFields()) as TBoundWitness
|
|
167
|
+
const $hash = (await PayloadBuilder.build(dataHashableFields)).$hash
|
|
168
|
+
const $meta = await this.metaFields($hash)
|
|
124
169
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
170
|
+
const ret = {
|
|
171
|
+
...dataHashableFields,
|
|
172
|
+
$hash,
|
|
173
|
+
$meta,
|
|
174
|
+
} as WithMeta<TBoundWitness>
|
|
175
|
+
return [ret, this._payloads, this._errors]
|
|
176
|
+
})
|
|
177
|
+
}
|
|
130
178
|
|
|
131
|
-
|
|
179
|
+
override async dataHashableFields(): Promise<Omit<TBoundWitness, '$meta' | '$hash'>> {
|
|
180
|
+
const fields = await this.linkingFields()
|
|
181
|
+
const result = await BoundWitnessBuilder.dataHashableFields<TBoundWitness>(this._schema, fields)
|
|
182
|
+
|
|
183
|
+
BoundWitnessBuilder.validateLinkingFields(result)
|
|
184
|
+
|
|
185
|
+
return result as Omit<TBoundWitness, '$meta' | '$hash'>
|
|
132
186
|
}
|
|
133
187
|
|
|
134
188
|
async error(payload?: ModuleError) {
|
|
@@ -196,14 +250,18 @@ export class BoundWitnessBuilder<T extends BoundWitness = BoundWitness, TPayload
|
|
|
196
250
|
return this
|
|
197
251
|
}
|
|
198
252
|
|
|
199
|
-
protected async signatures(_hash: Hash, previousHashes: (Hash | ArrayBuffer |
|
|
253
|
+
protected async signatures(_hash: Hash, previousHashes: (Hash | ArrayBuffer | null)[]): Promise<string[]> {
|
|
200
254
|
const hash = toArrayBuffer(_hash)
|
|
201
255
|
const previousHashesBytes = previousHashes.map((ph) => (ph ? toUint8Array(ph) : undefined))
|
|
202
256
|
return await Promise.all(this._accounts.map(async (account, index) => hexFromArrayBuffer(await account.sign(hash, previousHashesBytes[index]))))
|
|
203
257
|
}
|
|
204
258
|
|
|
205
|
-
private async
|
|
206
|
-
return
|
|
259
|
+
private async linkingFields() {
|
|
260
|
+
return await BoundWitnessBuilder.linkingFields<TBoundWitness>(this._accounts, this._payloads, this.timestamp)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private async metaFields(dataHash: Hash): Promise<JsonObject> {
|
|
264
|
+
return await BoundWitnessBuilder.metaFields(dataHash, this._$meta, this._accounts, this.previousHashes, this._destination, this._sourceQuery)
|
|
207
265
|
}
|
|
208
266
|
|
|
209
267
|
private missingSchemaMessage(payload: Payload) {
|