@xyo-network/boundwitness 2.21.11
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/LICENSE +165 -0
- package/README.md +79 -0
- package/babel.config.json +5 -0
- package/dist/cjs/Builder/Builder.d.ts +23 -0
- package/dist/cjs/Builder/Builder.js +68 -0
- package/dist/cjs/Builder/Builder.js.map +1 -0
- package/dist/cjs/Builder/index.d.ts +1 -0
- package/dist/cjs/Builder/index.js +5 -0
- package/dist/cjs/Builder/index.js.map +1 -0
- package/dist/cjs/Test/index.d.ts +1 -0
- package/dist/cjs/Test/index.js +5 -0
- package/dist/cjs/Test/index.js.map +1 -0
- package/dist/cjs/Test/testBoundWitness.d.ts +3 -0
- package/dist/cjs/Test/testBoundWitness.js +17 -0
- package/dist/cjs/Test/testBoundWitness.js.map +1 -0
- package/dist/cjs/Validator/BodyValidator.d.ts +17 -0
- package/dist/cjs/Validator/BodyValidator.js +94 -0
- package/dist/cjs/Validator/BodyValidator.js.map +1 -0
- package/dist/cjs/Validator/MetaValidator.d.ts +13 -0
- package/dist/cjs/Validator/MetaValidator.js +67 -0
- package/dist/cjs/Validator/MetaValidator.js.map +1 -0
- package/dist/cjs/Validator/Validator.d.ts +12 -0
- package/dist/cjs/Validator/Validator.js +47 -0
- package/dist/cjs/Validator/Validator.js.map +1 -0
- package/dist/cjs/Validator/index.d.ts +3 -0
- package/dist/cjs/Validator/index.js +7 -0
- package/dist/cjs/Validator/index.js.map +1 -0
- package/dist/cjs/Validator/ip-regex.d.ts +9 -0
- package/dist/cjs/Validator/ip-regex.js +31 -0
- package/dist/cjs/Validator/ip-regex.js.map +1 -0
- package/dist/cjs/Validator/is-ip.d.ts +4 -0
- package/dist/cjs/Validator/is-ip.js +21 -0
- package/dist/cjs/Validator/is-ip.js.map +1 -0
- package/dist/cjs/Wrapper/Wrapper.d.ts +11 -0
- package/dist/cjs/Wrapper/Wrapper.js +27 -0
- package/dist/cjs/Wrapper/Wrapper.js.map +1 -0
- package/dist/cjs/Wrapper/index.d.ts +1 -0
- package/dist/cjs/Wrapper/index.js +5 -0
- package/dist/cjs/Wrapper/index.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +9 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/isXyoPayloadOfSchemaType.d.ts +2 -0
- package/dist/cjs/isXyoPayloadOfSchemaType.js +6 -0
- package/dist/cjs/isXyoPayloadOfSchemaType.js.map +1 -0
- package/dist/cjs/models/Base/XyoBoundWitnessBase.d.ts +9 -0
- package/dist/cjs/models/Base/XyoBoundWitnessBase.js +3 -0
- package/dist/cjs/models/Base/XyoBoundWitnessBase.js.map +1 -0
- package/dist/cjs/models/Base/XyoBoundWitnessBaseSchema.d.ts +36 -0
- package/dist/cjs/models/Base/XyoBoundWitnessBaseSchema.js +19 -0
- package/dist/cjs/models/Base/XyoBoundWitnessBaseSchema.js.map +1 -0
- package/dist/cjs/models/Base/index.d.ts +2 -0
- package/dist/cjs/models/Base/index.js +6 -0
- package/dist/cjs/models/Base/index.js.map +1 -0
- package/dist/cjs/models/Meta/XyoBoundWitnessMeta.d.ts +7 -0
- package/dist/cjs/models/Meta/XyoBoundWitnessMeta.js +3 -0
- package/dist/cjs/models/Meta/XyoBoundWitnessMeta.js.map +1 -0
- package/dist/cjs/models/Meta/XyoBoundWitnessMetaSchema.d.ts +85 -0
- package/dist/cjs/models/Meta/XyoBoundWitnessMetaSchema.js +28 -0
- package/dist/cjs/models/Meta/XyoBoundWitnessMetaSchema.js.map +1 -0
- package/dist/cjs/models/Meta/index.d.ts +2 -0
- package/dist/cjs/models/Meta/index.js +6 -0
- package/dist/cjs/models/Meta/index.js.map +1 -0
- package/dist/cjs/models/WithXyoBoundWitnessMeta.d.ts +4 -0
- package/dist/cjs/models/WithXyoBoundWitnessMeta.js +3 -0
- package/dist/cjs/models/WithXyoBoundWitnessMeta.js.map +1 -0
- package/dist/cjs/models/XyoBoundWitness.d.ts +6 -0
- package/dist/cjs/models/XyoBoundWitness.js +3 -0
- package/dist/cjs/models/XyoBoundWitness.js.map +1 -0
- package/dist/cjs/models/XyoBoundWitnessSchema.d.ts +75 -0
- package/dist/cjs/models/XyoBoundWitnessSchema.js +17 -0
- package/dist/cjs/models/XyoBoundWitnessSchema.js.map +1 -0
- package/dist/cjs/models/index.d.ts +5 -0
- package/dist/cjs/models/index.js +9 -0
- package/dist/cjs/models/index.js.map +1 -0
- package/dist/docs.json +7 -0
- package/dist/esm/Builder/Builder.d.ts +23 -0
- package/dist/esm/Builder/Builder.js +66 -0
- package/dist/esm/Builder/Builder.js.map +1 -0
- package/dist/esm/Builder/index.d.ts +1 -0
- package/dist/esm/Builder/index.js +2 -0
- package/dist/esm/Builder/index.js.map +1 -0
- package/dist/esm/Test/index.d.ts +1 -0
- package/dist/esm/Test/index.js +2 -0
- package/dist/esm/Test/index.js.map +1 -0
- package/dist/esm/Test/testBoundWitness.d.ts +3 -0
- package/dist/esm/Test/testBoundWitness.js +14 -0
- package/dist/esm/Test/testBoundWitness.js.map +1 -0
- package/dist/esm/Validator/BodyValidator.d.ts +17 -0
- package/dist/esm/Validator/BodyValidator.js +88 -0
- package/dist/esm/Validator/BodyValidator.js.map +1 -0
- package/dist/esm/Validator/MetaValidator.d.ts +13 -0
- package/dist/esm/Validator/MetaValidator.js +63 -0
- package/dist/esm/Validator/MetaValidator.js.map +1 -0
- package/dist/esm/Validator/Validator.d.ts +12 -0
- package/dist/esm/Validator/Validator.js +42 -0
- package/dist/esm/Validator/Validator.js.map +1 -0
- package/dist/esm/Validator/index.d.ts +3 -0
- package/dist/esm/Validator/index.js +4 -0
- package/dist/esm/Validator/index.js.map +1 -0
- package/dist/esm/Validator/ip-regex.d.ts +9 -0
- package/dist/esm/Validator/ip-regex.js +27 -0
- package/dist/esm/Validator/ip-regex.js.map +1 -0
- package/dist/esm/Validator/is-ip.d.ts +4 -0
- package/dist/esm/Validator/is-ip.js +14 -0
- package/dist/esm/Validator/is-ip.js.map +1 -0
- package/dist/esm/Wrapper/Wrapper.d.ts +11 -0
- package/dist/esm/Wrapper/Wrapper.js +20 -0
- package/dist/esm/Wrapper/Wrapper.js.map +1 -0
- package/dist/esm/Wrapper/index.d.ts +1 -0
- package/dist/esm/Wrapper/index.js +2 -0
- package/dist/esm/Wrapper/index.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/isXyoPayloadOfSchemaType.d.ts +2 -0
- package/dist/esm/isXyoPayloadOfSchemaType.js +3 -0
- package/dist/esm/isXyoPayloadOfSchemaType.js.map +1 -0
- package/dist/esm/models/Base/XyoBoundWitnessBase.d.ts +9 -0
- package/dist/esm/models/Base/XyoBoundWitnessBase.js +2 -0
- package/dist/esm/models/Base/XyoBoundWitnessBase.js.map +1 -0
- package/dist/esm/models/Base/XyoBoundWitnessBaseSchema.d.ts +36 -0
- package/dist/esm/models/Base/XyoBoundWitnessBaseSchema.js +16 -0
- package/dist/esm/models/Base/XyoBoundWitnessBaseSchema.js.map +1 -0
- package/dist/esm/models/Base/index.d.ts +2 -0
- package/dist/esm/models/Base/index.js +3 -0
- package/dist/esm/models/Base/index.js.map +1 -0
- package/dist/esm/models/Meta/XyoBoundWitnessMeta.d.ts +7 -0
- package/dist/esm/models/Meta/XyoBoundWitnessMeta.js +2 -0
- package/dist/esm/models/Meta/XyoBoundWitnessMeta.js.map +1 -0
- package/dist/esm/models/Meta/XyoBoundWitnessMetaSchema.d.ts +85 -0
- package/dist/esm/models/Meta/XyoBoundWitnessMetaSchema.js +23 -0
- package/dist/esm/models/Meta/XyoBoundWitnessMetaSchema.js.map +1 -0
- package/dist/esm/models/Meta/index.d.ts +2 -0
- package/dist/esm/models/Meta/index.js +3 -0
- package/dist/esm/models/Meta/index.js.map +1 -0
- package/dist/esm/models/WithXyoBoundWitnessMeta.d.ts +4 -0
- package/dist/esm/models/WithXyoBoundWitnessMeta.js +2 -0
- package/dist/esm/models/WithXyoBoundWitnessMeta.js.map +1 -0
- package/dist/esm/models/XyoBoundWitness.d.ts +6 -0
- package/dist/esm/models/XyoBoundWitness.js +2 -0
- package/dist/esm/models/XyoBoundWitness.js.map +1 -0
- package/dist/esm/models/XyoBoundWitnessSchema.d.ts +75 -0
- package/dist/esm/models/XyoBoundWitnessSchema.js +21 -0
- package/dist/esm/models/XyoBoundWitnessSchema.js.map +1 -0
- package/dist/esm/models/index.d.ts +5 -0
- package/dist/esm/models/index.js +6 -0
- package/dist/esm/models/index.js.map +1 -0
- package/jest.config.cjs +25 -0
- package/package.json +61 -0
- package/src/Builder/Builder.spec.ts +88 -0
- package/src/Builder/Builder.ts +83 -0
- package/src/Builder/index.ts +1 -0
- package/src/Test/index.ts +1 -0
- package/src/Test/testBoundWitness.ts +16 -0
- package/src/Validator/BodyValidator.spec.ts +11 -0
- package/src/Validator/BodyValidator.ts +102 -0
- package/src/Validator/MetaValidator.spec.ts +56 -0
- package/src/Validator/MetaValidator.ts +71 -0
- package/src/Validator/Validator.spec.ts +11 -0
- package/src/Validator/Validator.ts +51 -0
- package/src/Validator/index.ts +3 -0
- package/src/Validator/ip-regex.ts +39 -0
- package/src/Validator/is-ip.ts +17 -0
- package/src/Wrapper/Wrapper.ts +27 -0
- package/src/Wrapper/index.ts +1 -0
- package/src/index.ts +5 -0
- package/src/isXyoPayloadOfSchemaType.ts +5 -0
- package/src/models/Base/XyoBoundWitnessBase.ts +10 -0
- package/src/models/Base/XyoBoundWitnessBaseSchema.ts +15 -0
- package/src/models/Base/index.ts +2 -0
- package/src/models/Meta/XyoBoundWitnessMeta.ts +8 -0
- package/src/models/Meta/XyoBoundWitnessMetaSchema.ts +24 -0
- package/src/models/Meta/index.ts +2 -0
- package/src/models/WithXyoBoundWitnessMeta.ts +6 -0
- package/src/models/XyoBoundWitness.ts +8 -0
- package/src/models/XyoBoundWitnessSchema.ts +23 -0
- package/src/models/index.ts +5 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { XyoHasher, XyoValidator } from '@xyo-network/core'
|
|
2
|
+
import { XyoPayload, XyoSchemaNameValidator } from '@xyo-network/payload'
|
|
3
|
+
import { validateType } from '@xyo-network/typeof'
|
|
4
|
+
import uniq from 'lodash/uniq'
|
|
5
|
+
|
|
6
|
+
import { XyoBoundWitness } from '../models'
|
|
7
|
+
|
|
8
|
+
export class XyoBoundWitnessBodyValidator<T extends XyoBoundWitness = XyoBoundWitness> extends XyoValidator<T> {
|
|
9
|
+
private payloads?: XyoPayload[]
|
|
10
|
+
constructor(boundWitness: T, payloads?: XyoPayload[]) {
|
|
11
|
+
super(boundWitness)
|
|
12
|
+
this.payloads = payloads
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public addressesUniqueness() {
|
|
16
|
+
const errors: Error[] = []
|
|
17
|
+
const { addresses } = this.obj
|
|
18
|
+
const uniqAddresses = uniq(addresses)
|
|
19
|
+
if (addresses?.length !== uniqAddresses?.length) errors.push(new Error('addresses must be unique'))
|
|
20
|
+
return errors
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public addresses() {
|
|
24
|
+
const errors: Error[] = []
|
|
25
|
+
const { addresses } = this.obj
|
|
26
|
+
if (!addresses?.length) errors.push(new Error('addresses missing [at least one address required]'))
|
|
27
|
+
errors.push(...this.addressesUniqueness())
|
|
28
|
+
return errors
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private validateArrayLength(fieldName: string, compareArrayName: string) {
|
|
32
|
+
const errors: Error[] = []
|
|
33
|
+
|
|
34
|
+
const [array, arrayErrors] = validateType('array', this.stringKeyObj[fieldName] as [], true)
|
|
35
|
+
const [compareArray, compareArrayErrors] = validateType('array', this.stringKeyObj[compareArrayName] as [], true)
|
|
36
|
+
|
|
37
|
+
if (array?.length !== compareArray?.length) {
|
|
38
|
+
errors.push(new Error(`${fieldName}/${compareArrayName} count mismatch [${array?.length} !== ${compareArray?.length}]`))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return [...arrayErrors, ...compareArrayErrors, ...errors]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public validatePayloadHashesLength() {
|
|
45
|
+
const errors: Error[] = []
|
|
46
|
+
errors.push(...this.validateArrayLength('payload_hashes', 'payload_schemas'))
|
|
47
|
+
return errors
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public validateArrayLengths() {
|
|
51
|
+
const errors: Error[] = []
|
|
52
|
+
errors.push(...this.validatePayloadHashesLength())
|
|
53
|
+
return errors
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public payloadHashes() {
|
|
57
|
+
const errors: Error[] = []
|
|
58
|
+
const passedHashes = this.obj.payload_hashes
|
|
59
|
+
this.payloads?.forEach((payload, index) => {
|
|
60
|
+
const calcHash = new XyoHasher(payload).hash
|
|
61
|
+
const passedHash = passedHashes?.[index]
|
|
62
|
+
if (calcHash !== passedHash) {
|
|
63
|
+
errors.push(new Error(`hash mismatch [${calcHash} !== ${passedHash}]`))
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
return errors
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public payloadSchemas() {
|
|
70
|
+
const errors: Error[] = []
|
|
71
|
+
const payloadSchemas = this.obj.payload_schemas
|
|
72
|
+
if (payloadSchemas) {
|
|
73
|
+
const schemaValidators: XyoSchemaNameValidator[] = payloadSchemas.map((schema: string) => {
|
|
74
|
+
return new XyoSchemaNameValidator(schema)
|
|
75
|
+
})
|
|
76
|
+
schemaValidators.forEach((validator) => {
|
|
77
|
+
errors.push(...validator.all())
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
return errors
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public previousHashes() {
|
|
84
|
+
const errors: Error[] = []
|
|
85
|
+
return errors
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public schema() {
|
|
89
|
+
const errors: Error[] = []
|
|
90
|
+
const expectedSchema = 'network.xyo.boundwitness'
|
|
91
|
+
if (this.obj.schema !== expectedSchema) {
|
|
92
|
+
errors.push(new Error(`invalid schema [${expectedSchema} !== ${this.obj.schema}]`))
|
|
93
|
+
}
|
|
94
|
+
return errors
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public validate() {
|
|
98
|
+
const errors: Error[] = []
|
|
99
|
+
errors.push(...this.addresses(), ...this.validateArrayLengths(), ...this.payloadHashes(), ...this.payloadSchemas(), ...this.previousHashes(), ...this.schema())
|
|
100
|
+
return errors
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { dumpErrors } from '@xyo-network/core'
|
|
2
|
+
|
|
3
|
+
import { XyoBoundWitnessWithMeta } from '../models'
|
|
4
|
+
import { testBoundWitness } from '../Test'
|
|
5
|
+
import { XyoBoundWitnessMetaValidator } from './MetaValidator'
|
|
6
|
+
|
|
7
|
+
describe('XyoBoundWitnessMetaValidator', () => {
|
|
8
|
+
it('Validates valid Meta', () => {
|
|
9
|
+
const validator = new XyoBoundWitnessMetaValidator(testBoundWitness)
|
|
10
|
+
const errors = validator.validate()
|
|
11
|
+
dumpErrors(errors)
|
|
12
|
+
expect(errors.length).toBe(0)
|
|
13
|
+
})
|
|
14
|
+
describe('sourceIp', () => {
|
|
15
|
+
it('Validates valid IPv4 Address', () => {
|
|
16
|
+
const bw: XyoBoundWitnessWithMeta = {
|
|
17
|
+
...testBoundWitness,
|
|
18
|
+
_source_ip: '127.0.0.1',
|
|
19
|
+
}
|
|
20
|
+
const validator = new XyoBoundWitnessMetaValidator(bw)
|
|
21
|
+
const errors = validator.validate()
|
|
22
|
+
dumpErrors(errors)
|
|
23
|
+
expect(errors.length).toBe(0)
|
|
24
|
+
})
|
|
25
|
+
it('Validates valid IPv6 Address', () => {
|
|
26
|
+
const bw: XyoBoundWitnessWithMeta = {
|
|
27
|
+
...testBoundWitness,
|
|
28
|
+
_source_ip: '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
|
29
|
+
}
|
|
30
|
+
const validator = new XyoBoundWitnessMetaValidator(bw)
|
|
31
|
+
const errors = validator.validate()
|
|
32
|
+
dumpErrors(errors)
|
|
33
|
+
expect(errors.length).toBe(0)
|
|
34
|
+
})
|
|
35
|
+
it('Handles missing Address', () => {
|
|
36
|
+
const bw: XyoBoundWitnessWithMeta = {
|
|
37
|
+
...testBoundWitness,
|
|
38
|
+
_source_ip: undefined,
|
|
39
|
+
}
|
|
40
|
+
const validator = new XyoBoundWitnessMetaValidator(bw)
|
|
41
|
+
const errors = validator.validate()
|
|
42
|
+
dumpErrors(errors)
|
|
43
|
+
expect(errors.length).toBe(0)
|
|
44
|
+
})
|
|
45
|
+
it('Detects invalid Addresses', () => {
|
|
46
|
+
const bw: XyoBoundWitnessWithMeta = {
|
|
47
|
+
...testBoundWitness,
|
|
48
|
+
_source_ip: 'notAnIpAddress',
|
|
49
|
+
}
|
|
50
|
+
const validator = new XyoBoundWitnessMetaValidator(bw)
|
|
51
|
+
const errors = validator.validate()
|
|
52
|
+
dumpErrors(errors)
|
|
53
|
+
expect(errors.length).toBe(0)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { XyoHasher, XyoValidator } from '@xyo-network/core'
|
|
2
|
+
|
|
3
|
+
import { XyoBoundWitnessMeta } from '../models'
|
|
4
|
+
import { isIP } from './is-ip'
|
|
5
|
+
|
|
6
|
+
const MIN_ALLOWED_TIMESTAMP = 1609459200000
|
|
7
|
+
const MAX_ALLOWED_TIMESTAMP = 4102444800000
|
|
8
|
+
|
|
9
|
+
export class XyoBoundWitnessMetaValidator<T extends Partial<XyoBoundWitnessMeta> = Partial<XyoBoundWitnessMeta>> extends XyoValidator<T> {
|
|
10
|
+
public client() {
|
|
11
|
+
const errors: Error[] = []
|
|
12
|
+
return errors
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public hash() {
|
|
16
|
+
const errors: Error[] = []
|
|
17
|
+
|
|
18
|
+
const hasher = new XyoHasher(this.obj)
|
|
19
|
+
|
|
20
|
+
const bodyHash = hasher.hash
|
|
21
|
+
if (bodyHash !== this.obj._hash) errors.push(new Error(`Body hash mismatch: [calculated: ${bodyHash}] [found: ${this.obj._hash}] [sortedStringify: ${hasher.stringified}]`))
|
|
22
|
+
return errors
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public payloads() {
|
|
26
|
+
const errors: Error[] = []
|
|
27
|
+
return errors
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public signatures() {
|
|
31
|
+
const errors: Error[] = []
|
|
32
|
+
return errors
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public timestamp() {
|
|
36
|
+
const errors: Error[] = []
|
|
37
|
+
const { _timestamp } = this.obj
|
|
38
|
+
if (_timestamp === undefined) errors.push(new Error('Missing _timestamp'))
|
|
39
|
+
else if (_timestamp < MIN_ALLOWED_TIMESTAMP) errors.push(new Error('_timestamp is before year 2021'))
|
|
40
|
+
else if (_timestamp > MAX_ALLOWED_TIMESTAMP) errors.push(new Error('_timestamp is after year 2100'))
|
|
41
|
+
return errors
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public sourceIp() {
|
|
45
|
+
const errors: Error[] = []
|
|
46
|
+
const { _source_ip } = this.obj
|
|
47
|
+
if (_source_ip && !isIP(_source_ip)) {
|
|
48
|
+
errors.push(new Error(`_source_ip invalid format [${_source_ip}]`))
|
|
49
|
+
}
|
|
50
|
+
return errors
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public userAgent() {
|
|
54
|
+
const errors: Error[] = []
|
|
55
|
+
const { _user_agent } = this.obj
|
|
56
|
+
if (!_user_agent) errors.push(new Error('_user_agent missing'))
|
|
57
|
+
return errors
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public validate() {
|
|
61
|
+
const errors: Error[] = []
|
|
62
|
+
errors.push(...this.client(), ...this.hash(), ...this.payloads(), ...this.signatures())
|
|
63
|
+
return errors
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public meta() {
|
|
67
|
+
const errors: Error[] = []
|
|
68
|
+
errors.push(...this.timestamp(), ...this.sourceIp(), ...this.userAgent())
|
|
69
|
+
return errors
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { dumpErrors } from '@xyo-network/core'
|
|
2
|
+
|
|
3
|
+
import { testBoundWitness } from '../Test'
|
|
4
|
+
import { XyoBoundWitnessValidator } from './Validator'
|
|
5
|
+
|
|
6
|
+
test('all', () => {
|
|
7
|
+
const validator = new XyoBoundWitnessValidator(testBoundWitness)
|
|
8
|
+
const errors = validator.validate()
|
|
9
|
+
dumpErrors(errors)
|
|
10
|
+
expect(errors.length).toBe(0)
|
|
11
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/sdk-js'
|
|
2
|
+
import { XyoAddressValue } from '@xyo-network/account'
|
|
3
|
+
import { XyoValidator } from '@xyo-network/core'
|
|
4
|
+
|
|
5
|
+
import { XyoBoundWitnessWithPartialMeta } from '../models'
|
|
6
|
+
import { XyoBoundWitnessBodyValidator } from './BodyValidator'
|
|
7
|
+
import { XyoBoundWitnessMetaValidator } from './MetaValidator'
|
|
8
|
+
|
|
9
|
+
const validateArraysSameLength = (a: unknown[], b: unknown[], message = 'Array length mismatch') => {
|
|
10
|
+
return a.length != b.length ? [Error(`${message} []`)] : []
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const validateSignature = (hash: string, address: string, signature?: string) => {
|
|
14
|
+
if (!signature) {
|
|
15
|
+
return [Error(`Missing signature [${address}]`)]
|
|
16
|
+
}
|
|
17
|
+
if (!new XyoAddressValue(address).verify(hash, signature)) {
|
|
18
|
+
return [Error(`Invalid signature [${address}] [${signature}]`)]
|
|
19
|
+
}
|
|
20
|
+
return []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class XyoBoundWitnessValidator<T extends XyoBoundWitnessWithPartialMeta = XyoBoundWitnessWithPartialMeta> extends XyoValidator<T> {
|
|
24
|
+
public body: XyoBoundWitnessBodyValidator
|
|
25
|
+
public meta: XyoBoundWitnessMetaValidator
|
|
26
|
+
constructor(bw: T) {
|
|
27
|
+
super(bw)
|
|
28
|
+
this.body = new XyoBoundWitnessBodyValidator(bw, bw._payloads)
|
|
29
|
+
this.meta = new XyoBoundWitnessMetaValidator(bw)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public signatures() {
|
|
33
|
+
const hash = assertEx(this.obj._hash, 'Missing _hash')
|
|
34
|
+
return [
|
|
35
|
+
...validateArraysSameLength(this.obj._signatures ?? [], this.obj.addresses, 'Length mismatch: address/_signature'),
|
|
36
|
+
...this.obj.addresses.reduce<Error[]>((errors, address, index) => {
|
|
37
|
+
errors.push(...validateSignature(hash, address, this.obj._signatures?.[index]))
|
|
38
|
+
return errors
|
|
39
|
+
}, []),
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public validate() {
|
|
44
|
+
const errors: Error[] = []
|
|
45
|
+
errors.push(...this.meta.validate(), ...this.body.validate())
|
|
46
|
+
errors.push(...this.signatures())
|
|
47
|
+
return errors
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { XyoBoundWitnessValidator }
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const word = '[a-fA-F\\d:]'
|
|
2
|
+
|
|
3
|
+
export interface Options {
|
|
4
|
+
readonly exact?: boolean
|
|
5
|
+
readonly includeBoundaries?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const boundry = (options: Options) => (options && options.includeBoundaries ? `(?:(?<=\\s|^)(?=${word})|(?<=${word})(?=\\s|$))` : '')
|
|
9
|
+
|
|
10
|
+
const v4 = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}'
|
|
11
|
+
|
|
12
|
+
const v6segment = '[a-fA-F\\d]{1,4}'
|
|
13
|
+
|
|
14
|
+
const v6 = `
|
|
15
|
+
(?:
|
|
16
|
+
(?:${v6segment}:){7}(?:${v6segment}|:)| // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
|
|
17
|
+
(?:${v6segment}:){6}(?:${v4}|:${v6segment}|:)| // 1:2:3:4:5:6:: 1:2:3:4:5:6::8 1:2:3:4:5:6::8 1:2:3:4:5:6::1.2.3.4
|
|
18
|
+
(?:${v6segment}:){5}(?::${v4}|(?::${v6segment}){1,2}|:)| // 1:2:3:4:5:: 1:2:3:4:5::7:8 1:2:3:4:5::8 1:2:3:4:5::7:1.2.3.4
|
|
19
|
+
(?:${v6segment}:){4}(?:(?::${v6segment}){0,1}:${v4}|(?::${v6segment}){1,3}|:)| // 1:2:3:4:: 1:2:3:4::6:7:8 1:2:3:4::8 1:2:3:4::6:7:1.2.3.4
|
|
20
|
+
(?:${v6segment}:){3}(?:(?::${v6segment}){0,2}:${v4}|(?::${v6segment}){1,4}|:)| // 1:2:3:: 1:2:3::5:6:7:8 1:2:3::8 1:2:3::5:6:7:1.2.3.4
|
|
21
|
+
(?:${v6segment}:){2}(?:(?::${v6segment}){0,3}:${v4}|(?::${v6segment}){1,5}|:)| // 1:2:: 1:2::4:5:6:7:8 1:2::8 1:2::4:5:6:7:1.2.3.4
|
|
22
|
+
(?:${v6segment}:){1}(?:(?::${v6segment}){0,4}:${v4}|(?::${v6segment}){1,6}|:)| // 1:: 1::3:4:5:6:7:8 1::8 1::3:4:5:6:7:1.2.3.4
|
|
23
|
+
(?::(?:(?::${v6segment}){0,5}:${v4}|(?::${v6segment}){1,7}|:)) // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::1.2.3.4
|
|
24
|
+
)(?:%[0-9a-zA-Z]{1,})? // %eth0 %1
|
|
25
|
+
`
|
|
26
|
+
.replace(/\s*\/\/.*$/gm, '')
|
|
27
|
+
.replace(/\n/g, '')
|
|
28
|
+
.trim()
|
|
29
|
+
|
|
30
|
+
// Pre-compile only the exact regexes because adding a global flag make regexes stateful
|
|
31
|
+
const v46Exact = new RegExp(`(?:^${v4}$)|(?:^${v6}$)`)
|
|
32
|
+
const v4exact = new RegExp(`^${v4}$`)
|
|
33
|
+
const v6exact = new RegExp(`^${v6}$`)
|
|
34
|
+
|
|
35
|
+
export const ipRegex = (options: Options) =>
|
|
36
|
+
options && options.exact ? v46Exact : new RegExp(`(?:${boundry(options)}${v4}${boundry(options)})|(?:${boundry(options)}${v6}${boundry(options)})`, 'g')
|
|
37
|
+
|
|
38
|
+
ipRegex.v4 = (options: Options) => (options && options.exact ? v4exact : new RegExp(`${boundry(options)}${v4}${boundry(options)}`, 'g'))
|
|
39
|
+
ipRegex.v6 = (options: Options) => (options && options.exact ? v6exact : new RegExp(`${boundry(options)}${v6}${boundry(options)}`, 'g'))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ipRegex } from './ip-regex'
|
|
2
|
+
|
|
3
|
+
export function isIP(value: string) {
|
|
4
|
+
return ipRegex({ exact: true }).test(value)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isIPv6(value: string) {
|
|
8
|
+
return ipRegex.v6({ exact: true }).test(value)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isIPv4(value: string) {
|
|
12
|
+
return ipRegex.v4({ exact: true }).test(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function ipVersion(value: string) {
|
|
16
|
+
return isIP(value) ? (isIPv6(value) ? 6 : 4) : undefined
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { XyoHasher } from '@xyo-network/core'
|
|
2
|
+
import pick from 'lodash/pick'
|
|
3
|
+
import { UAParser } from 'ua-parser-js'
|
|
4
|
+
|
|
5
|
+
import { XyoBoundWitness, XyoBoundWitnessWithMeta } from '../models'
|
|
6
|
+
|
|
7
|
+
const scrubbedFields = ['_archive', '_client', '_hash', '_signatures', '_timestamp', '_user_agent', 'addresses', 'payload_schemas', 'previous_hashes', 'payload_hashes', 'schema']
|
|
8
|
+
|
|
9
|
+
export class XyoBoundWitnessWrapper<T extends XyoBoundWitness> extends XyoHasher<T> {
|
|
10
|
+
public readonly bw: T
|
|
11
|
+
constructor(bw: T) {
|
|
12
|
+
super(bw)
|
|
13
|
+
this.bw = bw
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get scrubbedFields() {
|
|
17
|
+
return pick(this.bw, scrubbedFields)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class XyoBoundWitnessWithMetaWrapper<T extends XyoBoundWitnessWithMeta> extends XyoBoundWitnessWrapper<T> {
|
|
22
|
+
private _userAgent?: UAParser
|
|
23
|
+
get userAgent() {
|
|
24
|
+
this._userAgent = this._userAgent ?? new UAParser(this.bw._user_agent)
|
|
25
|
+
return this._userAgent
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Wrapper'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { XyoPayload } from '@xyo-network/payload'
|
|
2
|
+
|
|
3
|
+
export type XyoBoundWitnessBase = XyoPayload<{
|
|
4
|
+
addresses: string[]
|
|
5
|
+
previous_hash?: string
|
|
6
|
+
payload_hashes: string[]
|
|
7
|
+
payload_schemas: string[]
|
|
8
|
+
previous_hashes: (string | null)[]
|
|
9
|
+
schema: string
|
|
10
|
+
}>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//Should type as JSONSchemaType<XyoBoundWitnessBody> once ajv/eslint issue is fixed
|
|
2
|
+
//https://github.com/microsoft/TypeScript/issues/44851
|
|
3
|
+
export const XyoBoundWitnessBaseSchema = {
|
|
4
|
+
$id: 'https://schemas.xyo.network/2.0/boundwitness/body',
|
|
5
|
+
additionalProperties: false,
|
|
6
|
+
properties: {
|
|
7
|
+
addresses: { items: { type: 'string' }, type: 'array' },
|
|
8
|
+
payload_hashes: { items: { type: 'string' }, type: 'array' },
|
|
9
|
+
payload_schemas: { items: { type: 'string' }, type: 'array' },
|
|
10
|
+
previous_hashes: { items: { nullable: true, type: 'string' }, type: 'array' },
|
|
11
|
+
schema: { type: 'string' },
|
|
12
|
+
},
|
|
13
|
+
required: ['addresses', 'payload_hashes', 'payload_schemas', 'previous_hashes', 'schema'],
|
|
14
|
+
type: 'object',
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { XyoPayloadMeta, XyoPayloadWithPartialMeta } from '@xyo-network/payload'
|
|
2
|
+
|
|
3
|
+
export type XyoBoundWitnessMeta<P extends XyoPayloadWithPartialMeta = XyoPayloadWithPartialMeta> = XyoPayloadMeta<{
|
|
4
|
+
_payloads?: P[]
|
|
5
|
+
_signatures?: string[]
|
|
6
|
+
_source_ip?: string
|
|
7
|
+
_user_agent?: string
|
|
8
|
+
}>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//Should type as JSONSchemaType<XyoBoundWitnessMeta> once ajv/eslint issue is fixed
|
|
2
|
+
//https://github.com/microsoft/TypeScript/issues/44851
|
|
3
|
+
|
|
4
|
+
export const XyoBoundWitnessMetaSchemaProperties = () => {
|
|
5
|
+
return {
|
|
6
|
+
_archive: { nullable: true, type: 'string' },
|
|
7
|
+
_client: { nullable: true, type: 'string' },
|
|
8
|
+
_hash: { nullable: true, type: 'string' },
|
|
9
|
+
_payloads: { items: { type: 'object' }, nullable: true, type: 'array' },
|
|
10
|
+
_signatures: { items: { type: 'string' }, nullable: true, type: 'array' },
|
|
11
|
+
_source_ip: { nullable: true, type: 'string' },
|
|
12
|
+
_timestamp: { nullable: true, type: 'number' },
|
|
13
|
+
_user_agent: { nullable: true, type: 'string' },
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const XyoBoundWitnessMetaSchema = () => {
|
|
18
|
+
return {
|
|
19
|
+
$id: 'https://schemas.xyo.network/2.0/boundwitness/meta',
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
properties: XyoBoundWitnessMetaSchemaProperties,
|
|
22
|
+
type: 'object',
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { XyoPayloadWithPartialMeta } from '@xyo-network/payload'
|
|
2
|
+
|
|
3
|
+
import { XyoBoundWitnessBase } from './Base'
|
|
4
|
+
import { XyoBoundWitnessMeta } from './Meta'
|
|
5
|
+
|
|
6
|
+
export type XyoBoundWitness<T extends object = object> = T & XyoBoundWitnessBase
|
|
7
|
+
export type XyoBoundWitnessWithMeta<T = unknown, P extends XyoPayloadWithPartialMeta = XyoPayloadWithPartialMeta> = T & XyoBoundWitnessMeta<P> & XyoBoundWitness
|
|
8
|
+
export type XyoBoundWitnessWithPartialMeta<T = unknown, P extends XyoPayloadWithPartialMeta = XyoPayloadWithPartialMeta> = T & Partial<XyoBoundWitnessMeta<P>> & XyoBoundWitness
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//Should type as JSONSchemaType<XyoBoundWitness> once ajv/eslint issue is fixed
|
|
2
|
+
//https://github.com/microsoft/TypeScript/issues/44851
|
|
3
|
+
|
|
4
|
+
import { XyoBoundWitnessMetaSchemaProperties } from './Meta'
|
|
5
|
+
|
|
6
|
+
const XyoBoundWitnessSchema = () => {
|
|
7
|
+
return {
|
|
8
|
+
$id: 'https://schemas.xyo.network/2.0/boundwitness',
|
|
9
|
+
additionalProperties: false,
|
|
10
|
+
properties: {
|
|
11
|
+
...XyoBoundWitnessMetaSchemaProperties(),
|
|
12
|
+
addresses: { items: { type: 'string' }, type: 'array' },
|
|
13
|
+
payload_hashes: { items: { type: 'string' }, type: 'array' },
|
|
14
|
+
payload_schemas: { items: { type: 'string' }, type: 'array' },
|
|
15
|
+
previous_hashes: { items: { nullable: true, type: 'string' }, type: 'array' },
|
|
16
|
+
schema: { type: 'string' },
|
|
17
|
+
},
|
|
18
|
+
required: ['addresses', 'payload_hashes', 'payload_schemas', 'previous_hashes', 'schema'],
|
|
19
|
+
type: 'object',
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { XyoBoundWitnessSchema }
|