@xyo-network/chain-validation 1.4.7 → 1.5.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/neutral/index.mjs +251 -172
- package/dist/neutral/index.mjs.map +1 -1
- package/dist/types/block/validateBlock.d.ts.map +1 -1
- package/dist/types/block/validators/AllowedPayloadSchemas.d.ts.map +1 -1
- package/dist/types/block/validators/Fields.d.ts.map +1 -1
- package/dist/types/block/validators/JsonSchema.d.ts.map +1 -1
- package/dist/types/block/validators/PreviousHash.d.ts.map +1 -1
- package/dist/types/elevatedPayload/lib/validateElevatedFromBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/lib/validateElevatedFromTransaction.d.ts.map +1 -1
- package/dist/types/elevatedPayload/lib/validateIncludedInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/lib/validateTransactionInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/lib/validateTypedPayloadInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/payloads/validateChainStakeIntentInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/payloads/validateHashInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/payloads/validateSchemaInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/payloads/validateTransferInBlock.d.ts.map +1 -1
- package/dist/types/elevatedPayload/validatePayloadInBlock.d.ts.map +1 -1
- package/dist/types/hydratedBlock/validateHydratedBlock.d.ts.map +1 -1
- package/dist/types/hydratedBlock/validators/Payloads.d.ts.map +1 -1
- package/dist/types/hydratedBlockState/validateHydratedBlockState.d.ts.map +1 -1
- package/dist/types/hydratedBlockState/validators/RequiredBalance.d.ts.map +1 -1
- package/dist/types/transaction/validateTransaction.d.ts.map +1 -1
- package/dist/types/transaction/validators/TransactionDurationValidator.d.ts.map +1 -1
- package/dist/types/transaction/validators/TransactionElevationValidator.d.ts.map +1 -1
- package/dist/types/transaction/validators/TransactionFromValidator.d.ts.map +1 -1
- package/dist/types/transaction/validators/TransactionGasValidator.d.ts.map +1 -1
- package/dist/types/transaction/validators/TransactionProtocolValidator.d.ts.map +1 -1
- package/package.json +24 -25
- package/src/block/validateBlock.ts +5 -4
- package/src/block/validators/AllowedPayloadSchemas.ts +7 -3
- package/src/block/validators/Fields.ts +6 -2
- package/src/block/validators/JsonSchema.ts +12 -8
- package/src/block/validators/PreviousHash.ts +15 -11
- package/src/elevatedPayload/lib/validateElevatedFromBlock.ts +9 -4
- package/src/elevatedPayload/lib/validateElevatedFromTransaction.ts +11 -7
- package/src/elevatedPayload/lib/validateIncludedInBlock.ts +6 -2
- package/src/elevatedPayload/lib/validateTransactionInBlock.ts +8 -4
- package/src/elevatedPayload/lib/validateTypedPayloadInBlock.ts +10 -6
- package/src/elevatedPayload/payloads/validateChainStakeIntentInBlock.ts +5 -1
- package/src/elevatedPayload/payloads/validateHashInBlock.ts +5 -1
- package/src/elevatedPayload/payloads/validateSchemaInBlock.ts +5 -1
- package/src/elevatedPayload/payloads/validateTransferInBlock.ts +5 -1
- package/src/elevatedPayload/validatePayloadInBlock.ts +9 -5
- package/src/hydratedBlock/validateHydratedBlock.ts +13 -7
- package/src/hydratedBlock/validators/Payloads.ts +21 -16
- package/src/hydratedBlockState/validateHydratedBlockState.ts +15 -8
- package/src/hydratedBlockState/validators/RequiredBalance.ts +25 -34
- package/src/transaction/validateTransaction.ts +17 -13
- package/src/transaction/validators/TransactionDurationValidator.ts +9 -5
- package/src/transaction/validators/TransactionElevationValidator.ts +7 -3
- package/src/transaction/validators/TransactionFromValidator.ts +7 -3
- package/src/transaction/validators/TransactionGasValidator.ts +18 -14
- package/src/transaction/validators/TransactionProtocolValidator.ts +1 -5
|
@@ -5,20 +5,24 @@ export const BlockPreviousHashValidator: BlockValidatorFunction = (
|
|
|
5
5
|
block: BlockBoundWitness,
|
|
6
6
|
) => {
|
|
7
7
|
const errors: Error[] = []
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
try {
|
|
9
|
+
const blockNumber = block.block
|
|
10
|
+
if (blockNumber > 0n) {
|
|
10
11
|
// if this is not the first block, validate previous hashes
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (!isHash(block.previous)) {
|
|
13
|
+
errors.push(new Error('previous hash is missing or invalid'))
|
|
14
|
+
}
|
|
15
|
+
} else if (blockNumber === 0) {
|
|
15
16
|
// if this is the first block, validate previous hashes
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
if (block.previous !== null) {
|
|
18
|
+
errors.push(new Error('previous hash should not be set'))
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
20
21
|
// we have a negative block number
|
|
21
|
-
|
|
22
|
+
errors.push(new Error('invalid block number'))
|
|
23
|
+
}
|
|
24
|
+
} catch (e) {
|
|
25
|
+
errors.push(new Error(`Failed BlockPreviousHashValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
22
26
|
}
|
|
23
27
|
return errors
|
|
24
28
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Promisable } from '@xylabs/promise'
|
|
2
|
-
import {
|
|
2
|
+
import { transactionsFromHydratedBlock } from '@xyo-network/chain-protocol'
|
|
3
3
|
import type { HydratedBlock, InBlockPayloadValidationFunction } from '@xyo-network/xl1-protocol'
|
|
4
4
|
|
|
5
5
|
export const validateElevatedFromBlock: InBlockPayloadValidationFunction = (
|
|
@@ -7,9 +7,14 @@ export const validateElevatedFromBlock: InBlockPayloadValidationFunction = (
|
|
|
7
7
|
block: HydratedBlock,
|
|
8
8
|
): Promisable<Error[]> => {
|
|
9
9
|
const errors: Error[] = []
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
try {
|
|
11
|
+
const txs = transactionsFromHydratedBlock(block)
|
|
12
|
+
const allTxPayloadHashes = new Set(txs.flatMap(tx => tx.payload_hashes))
|
|
13
|
+
if (allTxPayloadHashes.has(payload._hash)) {
|
|
14
|
+
errors.push(new Error(`Transaction may not include block payload hash [${payload.schema}]: ${payload._hash}`))
|
|
15
|
+
}
|
|
16
|
+
} catch (e) {
|
|
17
|
+
errors.push(new Error(`Failed validateElevatedFromBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
13
18
|
}
|
|
14
19
|
return errors
|
|
15
20
|
}
|
|
@@ -7,14 +7,18 @@ export const validateElevatedFromTransaction: InBlockPayloadValidationFunction =
|
|
|
7
7
|
block: HydratedBlock,
|
|
8
8
|
): Promisable<Error[]> => {
|
|
9
9
|
const errors: Error[] = []
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
try {
|
|
11
|
+
const txs = transactionsFromHydratedBlock(block)
|
|
12
|
+
if (txs.length > 0) {
|
|
13
|
+
const hashes = txs.flatMap(tx => tx.payload_hashes)
|
|
14
|
+
if (!hashes.includes(payload._hash)) {
|
|
15
|
+
errors.push(new Error('Transaction does not include payload'))
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
errors.push(new Error('No Transactions in block'))
|
|
15
19
|
}
|
|
16
|
-
}
|
|
17
|
-
errors.push(new Error(
|
|
20
|
+
} catch (e) {
|
|
21
|
+
errors.push(new Error(`Failed validateElevatedFromTransaction: ${e instanceof Error ? e.message : String(e)}`))
|
|
18
22
|
}
|
|
19
23
|
return errors
|
|
20
24
|
}
|
|
@@ -3,8 +3,12 @@ import type { BlockBoundWitness } from '@xyo-network/xl1-protocol'
|
|
|
3
3
|
|
|
4
4
|
export const validateIncludedInBlock = (payload: WithStorageMeta<Payload>, block: BlockBoundWitness): Error[] => {
|
|
5
5
|
const errors: Error[] = []
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
try {
|
|
7
|
+
if (!block.payload_hashes.includes(payload._hash)) {
|
|
8
|
+
errors.push(new Error('Payload not included in block'))
|
|
9
|
+
}
|
|
10
|
+
} catch (e) {
|
|
11
|
+
errors.push(new Error(`Failed validateIncludedInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
8
12
|
}
|
|
9
13
|
return errors
|
|
10
14
|
}
|
|
@@ -9,10 +9,14 @@ export const validateTransactionInBlock: InBlockPayloadValidationFunction = asyn
|
|
|
9
9
|
_block: HydratedBlock,
|
|
10
10
|
): Promise<Error[]> => {
|
|
11
11
|
const errors: Error[] = []
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
try {
|
|
13
|
+
if (isTransactionBoundWitness(payload) && isStorageMeta(payload)) {
|
|
14
|
+
errors.push(...await BoundWitnessSignaturesValidator(payload))
|
|
15
|
+
} else {
|
|
16
|
+
errors.push(new Error('Payload failed isTransactionBoundWitness or isStorageMeta'))
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
errors.push(new Error(`Failed validateTransactionInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
16
20
|
}
|
|
17
21
|
return errors
|
|
18
22
|
}
|
|
@@ -13,14 +13,18 @@ export const validateTypedPayloadInBlock = async <T extends Payload>(
|
|
|
13
13
|
identityFunction: IdentityFunction<T>,
|
|
14
14
|
): Promise<Error[]> => {
|
|
15
15
|
const errors: Error[] = []
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
16
|
+
try {
|
|
17
|
+
if (identityFunction(payload) && isStorageMeta(payload)) {
|
|
18
|
+
if (isElevatedFromBlock(payload, block)) {
|
|
19
|
+
errors.push(...await validateElevatedFromBlock(payload, block))
|
|
20
|
+
} else {
|
|
21
|
+
errors.push(...await validateElevatedFromTransaction(payload, block))
|
|
22
|
+
}
|
|
19
23
|
} else {
|
|
20
|
-
errors.push(
|
|
24
|
+
errors.push(new Error('Payload failed identityFunction or isElevated or isStorageMeta'))
|
|
21
25
|
}
|
|
22
|
-
}
|
|
23
|
-
errors.push(new Error(
|
|
26
|
+
} catch (e) {
|
|
27
|
+
errors.push(new Error(`Failed validateTypedPayloadInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
24
28
|
}
|
|
25
29
|
return errors
|
|
26
30
|
}
|
|
@@ -8,6 +8,10 @@ export const validateChainStakeIntentInBlock: InBlockPayloadValidationFunction =
|
|
|
8
8
|
block: HydratedBlock,
|
|
9
9
|
): Promise<Error[]> => {
|
|
10
10
|
const errors: Error[] = []
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
errors.push(...await validateTypedPayloadInBlock(payload, block, isChainStakeIntent))
|
|
13
|
+
} catch (e) {
|
|
14
|
+
errors.push(new Error(`Failed validateChainStakeIntentInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
15
|
+
}
|
|
12
16
|
return errors
|
|
13
17
|
}
|
|
@@ -8,6 +8,10 @@ export const validateHashInBlock: InBlockPayloadValidationFunction = async (
|
|
|
8
8
|
block: HydratedBlock,
|
|
9
9
|
): Promise<Error[]> => {
|
|
10
10
|
const errors: Error[] = []
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
errors.push(...await validateTypedPayloadInBlock(payload, block, isHashPayload))
|
|
13
|
+
} catch (e) {
|
|
14
|
+
errors.push(new Error(`Failed validateHashInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
15
|
+
}
|
|
12
16
|
return errors
|
|
13
17
|
}
|
|
@@ -8,6 +8,10 @@ export const validateSchemaInBlock: InBlockPayloadValidationFunction = async (
|
|
|
8
8
|
block: HydratedBlock,
|
|
9
9
|
): Promise<Error[]> => {
|
|
10
10
|
const errors: Error[] = []
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
errors.push(...await validateTypedPayloadInBlock(payload, block, isSchemaPayload))
|
|
13
|
+
} catch (e) {
|
|
14
|
+
errors.push(new Error(`Failed validateSchemaInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
15
|
+
}
|
|
12
16
|
return errors
|
|
13
17
|
}
|
|
@@ -9,6 +9,10 @@ export const validateTransferInBlock: InBlockPayloadValidationFunction = async (
|
|
|
9
9
|
block: HydratedBlock,
|
|
10
10
|
): Promise<Error[]> => {
|
|
11
11
|
const errors: Error[] = []
|
|
12
|
-
|
|
12
|
+
try {
|
|
13
|
+
errors.push(...await validateTypedPayloadInBlock(payload, block, isTransfer))
|
|
14
|
+
} catch (e) {
|
|
15
|
+
errors.push(new Error(`Failed validateTransferInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
16
|
+
}
|
|
13
17
|
return errors
|
|
14
18
|
}
|
|
@@ -27,11 +27,15 @@ export const validatePayloadInBlock: InBlockPayloadValidationFunction = async (
|
|
|
27
27
|
block: HydratedBlock,
|
|
28
28
|
): Promise<Error[]> => {
|
|
29
29
|
const errors: Error[] = []
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
try {
|
|
31
|
+
const validator = payloadValidators[payload.schema]
|
|
32
|
+
if (validator) {
|
|
33
|
+
errors.push(...await validator(payload, block))
|
|
34
|
+
} else {
|
|
35
|
+
errors.push(new Error(`Unsupported payload schema: ${payload.schema}`))
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
errors.push(new Error(`Failed validatePayloadInBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
35
39
|
}
|
|
36
40
|
return errors
|
|
37
41
|
}
|
|
@@ -10,11 +10,17 @@ export const validateHydratedBlock: HydratedBlockValidatorFunction = async (
|
|
|
10
10
|
chainId?: Address,
|
|
11
11
|
additionalValidators: HydratedBlockValidatorFunction[] = [],
|
|
12
12
|
): Promise<Error[]> => {
|
|
13
|
-
const errors: Error[] =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
const errors: Error[] = []
|
|
14
|
+
try {
|
|
15
|
+
errors.push(...await validateBlock(hydratedBlock[0], chainId))
|
|
16
|
+
const validators: HydratedBlockValidatorFunction[] = [
|
|
17
|
+
BoundWitnessReferencesValidator(),
|
|
18
|
+
PayloadsInBlockValidator,
|
|
19
|
+
...additionalValidators,
|
|
20
|
+
]
|
|
21
|
+
errors.push(...(await Promise.all(validators.map(v => v(hydratedBlock, chainId)))).flat())
|
|
22
|
+
} catch (e) {
|
|
23
|
+
errors.push(new Error(`Failed validateHydratedBlock: ${e instanceof Error ? e.message : String(e)}`))
|
|
24
|
+
}
|
|
25
|
+
return errors
|
|
20
26
|
}
|
|
@@ -11,26 +11,31 @@ export const PayloadsInBlockValidator: HydratedBlockValidatorFunction = async (
|
|
|
11
11
|
[block, payloads]: HydratedBlock,
|
|
12
12
|
) => {
|
|
13
13
|
const errors: Error[] = []
|
|
14
|
+
try {
|
|
15
|
+
const payloadMap: Partial<Record<Hash, WithHashStorageMeta<Payload>>> = {}
|
|
16
|
+
for (const payload of payloads) {
|
|
17
|
+
payloadMap[payload._hash] = payload
|
|
18
|
+
}
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
for (const payload of payloads) {
|
|
17
|
-
payloadMap[payload._hash] = payload
|
|
18
|
-
}
|
|
20
|
+
const remainingPayloads = { ...payloadMap }
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
for (let i = 0; i < block.payload_hashes.length; i++) {
|
|
23
|
+
const hash = block.payload_hashes[i]
|
|
24
|
+
const schema = block.payload_schemas[i]
|
|
25
|
+
const payload = payloadMap[hash]
|
|
26
|
+
if (payload) {
|
|
27
|
+
errors.push(...await validatePayloadInBlock(payload, [block, payloads]))
|
|
28
|
+
delete remainingPayloads[hash]
|
|
29
|
+
} else {
|
|
30
|
+
errors.push(new Error(`missing payload ${hash} ${schema}`))
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
|
-
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
if (Object.keys(remainingPayloads).length > 0) {
|
|
35
|
+
errors.push(new Error(`extra payloads ${Object.keys(payloadMap).join(', ')}`))
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
errors.push(new Error(`Failed PayloadsInBlockValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
return errors
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Address } from '@xylabs/hex'
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
AccountBalanceService, HydratedBlock, HydratedBlockStateValidationFunction,
|
|
4
|
+
} from '@xyo-network/xl1-protocol'
|
|
4
5
|
|
|
5
6
|
import { validateHydratedBlock } from '../hydratedBlock/index.ts'
|
|
6
7
|
import { RequiredBalanceBlockStateValidator } from './validators/index.ts'
|
|
@@ -8,13 +9,19 @@ import { RequiredBalanceBlockStateValidator } from './validators/index.ts'
|
|
|
8
9
|
export const validateHydratedBlockState: HydratedBlockStateValidationFunction = async (
|
|
9
10
|
hydratedBlock: HydratedBlock,
|
|
10
11
|
chainId: Address,
|
|
11
|
-
|
|
12
|
+
services: { accountBalance: AccountBalanceService },
|
|
12
13
|
additionalValidators: HydratedBlockStateValidationFunction[] = [],
|
|
13
14
|
): Promise<Error[]> => {
|
|
14
|
-
const errors: Error[] =
|
|
15
|
-
|
|
15
|
+
const errors: Error[] = []
|
|
16
|
+
try {
|
|
17
|
+
errors.push(...await validateHydratedBlock(hydratedBlock, chainId))
|
|
18
|
+
const validators: HydratedBlockStateValidationFunction[] = [
|
|
16
19
|
// RequiredBalanceBlockStateValidator,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
...additionalValidators,
|
|
21
|
+
]
|
|
22
|
+
errors.push(...(await Promise.all(validators.map(v => v(hydratedBlock, chainId, services)))).flat())
|
|
23
|
+
} catch (e) {
|
|
24
|
+
errors.push(new Error(`Failed validateHydratedBlockState: ${e instanceof Error ? e.message : String(e)}`))
|
|
25
|
+
}
|
|
26
|
+
return errors
|
|
20
27
|
}
|
|
@@ -1,46 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '@xyo-network/chain-analyze'
|
|
7
|
-
import { parseSignedBigInt } from '@xyo-network/chain-protocol'
|
|
8
|
-
import type { HydratedBlock, HydratedBlockStateValidationFunction } from '@xyo-network/xl1-protocol'
|
|
1
|
+
import { type Address, hexToBigInt } from '@xylabs/hex'
|
|
2
|
+
import { netBalancesForPayloads } from '@xyo-network/chain-protocol'
|
|
3
|
+
import type {
|
|
4
|
+
AccountBalanceService, HydratedBlock, HydratedBlockStateValidationFunction,
|
|
5
|
+
} from '@xyo-network/xl1-protocol'
|
|
9
6
|
|
|
10
7
|
export const RequiredBalanceBlockStateValidator: HydratedBlockStateValidationFunction = async (
|
|
11
8
|
block: HydratedBlock,
|
|
12
9
|
chainId: Address,
|
|
13
|
-
|
|
10
|
+
services: { accountBalance: AccountBalanceService },
|
|
14
11
|
) => {
|
|
15
12
|
const errors: Error[] = []
|
|
16
|
-
|
|
13
|
+
try {
|
|
17
14
|
// TODO: Filter by non-producer elevated payloads
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
// to allow for transfers from ZERO address
|
|
16
|
+
const netBalances = netBalancesForPayloads(block[1])
|
|
17
|
+
const netBalanceAddresses = Object.keys(netBalances) as Address[]
|
|
18
|
+
const requiredBalances: Record<Address, bigint> = {}
|
|
19
|
+
for (const address of netBalanceAddresses) {
|
|
20
|
+
if (netBalances[address] < 0n) {
|
|
21
|
+
requiredBalances[address] = -netBalances[address]
|
|
22
|
+
}
|
|
25
23
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (previous === null) return [new Error('Insufficient funds because first block')]
|
|
29
|
-
const results = await analyzeChain(
|
|
30
|
-
archivist,
|
|
31
|
-
[
|
|
32
|
-
new BalanceAnalyzer(Object.keys(requiredBalances) as Address[], Object.values(requiredBalances)),
|
|
33
|
-
],
|
|
34
|
-
previous,
|
|
35
|
-
)
|
|
36
|
-
const summaryBalances = assertEx(results.find(isChainSummaryBalances), () => new Error('missing summary balances'))
|
|
24
|
+
const previous = block[0].previous
|
|
25
|
+
if (previous === null) return [new Error('Insufficient funds because first block')]
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
for (const [address, reqBalance] of Object.entries(requiredBalances) as [Address, bigint][]) {
|
|
28
|
+
const balance = hexToBigInt(services.accountBalance.getBalance(address))
|
|
29
|
+
if (reqBalance > balance) {
|
|
30
|
+
errors.push(new Error(`insufficient balance for ${address} ${balance} < ${requiredBalances[address]}`))
|
|
31
|
+
}
|
|
42
32
|
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
errors.push(new Error(`Failed RequiredBalanceBlockStateValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
43
35
|
}
|
|
44
|
-
|
|
45
|
-
return errors
|
|
36
|
+
return await Promise.resolve(errors)
|
|
46
37
|
}
|
|
@@ -12,18 +12,22 @@ export async function validateTransaction(
|
|
|
12
12
|
chainId?: Address,
|
|
13
13
|
additionalValidators: HydratedTransactionValidatorFunction[] = [],
|
|
14
14
|
): Promise<Error[]> {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
try {
|
|
16
|
+
if (!isTransactionBoundWitness(tx[0])) {
|
|
17
|
+
return [new Error('failed isTransactionBoundWitness identity check')]
|
|
18
|
+
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
const validators: HydratedTransactionValidatorFunction[] = [
|
|
21
|
+
TransactionProtocolValidator,
|
|
22
|
+
TransactionJsonSchemaValidator,
|
|
23
|
+
TransactionDurationValidator,
|
|
24
|
+
TransactionFromValidator,
|
|
25
|
+
TransactionGasValidator,
|
|
26
|
+
TransactionElevationValidator,
|
|
27
|
+
...additionalValidators,
|
|
28
|
+
]
|
|
29
|
+
return (await Promise.all(validators.map(v => v(tx, chainId)))).flat()
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return [(new Error(`Failed TransactionGasValidator: ${e instanceof Error ? e.message : String(e)}`))]
|
|
32
|
+
}
|
|
29
33
|
}
|
|
@@ -6,12 +6,16 @@ export const TransactionDurationValidator: HydratedTransactionValidatorFunction<
|
|
|
6
6
|
[tx]: HydratedTransaction,
|
|
7
7
|
): Error[] => {
|
|
8
8
|
const errors: Error[] = []
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
try {
|
|
10
|
+
const { exp, nbf } = tx
|
|
11
|
+
if (nbf < 0) errors.push(new Error('Transaction nbf must be positive'))
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
if (exp < 0) errors.push(new Error('Transaction exp must be positive'))
|
|
14
|
+
if (exp <= nbf) errors.push(new Error('Transaction exp must greater than nbf'))
|
|
15
|
+
if (exp - nbf > 10_000) errors.push(new Error('Transaction exp must not be too far in the future'))
|
|
16
|
+
} catch (e) {
|
|
17
|
+
errors.push(new Error(`Failed TransactionDurationValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
18
|
+
}
|
|
15
19
|
|
|
16
20
|
return errors
|
|
17
21
|
}
|
|
@@ -9,9 +9,13 @@ export const TransactionElevationValidator: HydratedTransactionValidatorFunction
|
|
|
9
9
|
): Promisable<Error[]> => {
|
|
10
10
|
const errors: Error[] = []
|
|
11
11
|
try {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
try {
|
|
13
|
+
extractElevatedHashes(tx)
|
|
14
|
+
} catch {
|
|
15
|
+
errors.push(new Error('Hydrated transaction does not include all script hashes'))
|
|
16
|
+
}
|
|
17
|
+
} catch (e) {
|
|
18
|
+
errors.push(new Error(`Failed TransactionElevationValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
15
19
|
}
|
|
16
20
|
return errors
|
|
17
21
|
}
|
|
@@ -8,8 +8,12 @@ export const TransactionFromValidator: HydratedTransactionValidatorFunction<Tran
|
|
|
8
8
|
[tx]: HydratedTransaction,
|
|
9
9
|
): Error[] => {
|
|
10
10
|
const errors: Error[] = []
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
try {
|
|
12
|
+
const from = asAddress(tx.from)
|
|
13
|
+
if (from === undefined)errors.push(new Error('Transaction from is not a valid address'))
|
|
14
|
+
else if (!addressesContains(tx, from)) errors.push(new Error('Transaction from address must be listed in addresses'))
|
|
15
|
+
} catch (e) {
|
|
16
|
+
errors.push(new Error(`Failed TransactionFromValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
17
|
+
}
|
|
14
18
|
return errors
|
|
15
19
|
}
|
|
@@ -10,24 +10,28 @@ export const TransactionGasValidator: HydratedTransactionValidatorFunction<Trans
|
|
|
10
10
|
[tx]: HydratedTransaction,
|
|
11
11
|
): Error[] => {
|
|
12
12
|
const errors: Error[] = []
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
try {
|
|
14
|
+
if (tx?.fees === undefined) {
|
|
15
|
+
errors.push(new Error('Missing fees'))
|
|
16
|
+
} else {
|
|
17
|
+
const {
|
|
18
|
+
base, gasLimit, gasPrice, priority,
|
|
19
|
+
} = parseFees(tx.fees)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
if (base === undefined) errors.push(new Error('fees.base must be defined and a valid number'))
|
|
22
|
+
else if (base < minTransactionFees.base) errors.push(new Error(`fees.base must be >= ${minTransactionFees.base}`))
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
if (gasLimit === undefined) errors.push(new Error('fees.gasLimit must be defined and a valid number'))
|
|
25
|
+
else if (gasLimit < minTransactionFees.gasLimit) errors.push(new Error(`fees.gasLimit must be >= ${minTransactionFees.gasLimit}`))
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
if (gasPrice === undefined) errors.push(new Error('fees.gasPrice must be defined and a valid number'))
|
|
28
|
+
else if (gasPrice < minTransactionFees.gasPrice) errors.push(new Error(`fees.gasPrice must be >= ${minTransactionFees.gasPrice}`))
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
if (priority === undefined) errors.push(new Error('fees.priority must be defined and a valid number'))
|
|
31
|
+
else if (priority < minTransactionFees.priority) errors.push(new Error(`fees.priority must be >= ${minTransactionFees.priority}`))
|
|
32
|
+
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
errors.push(new Error(`Failed TransactionGasValidator: ${e instanceof Error ? e.message : String(e)}`))
|
|
31
35
|
}
|
|
32
36
|
return errors
|
|
33
37
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Address } from '@xylabs/hex'
|
|
2
|
-
import { BoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
|
|
3
2
|
import type {
|
|
4
3
|
HydratedTransaction, HydratedTransactionValidatorFunction, TransactionBoundWitness,
|
|
5
4
|
} from '@xyo-network/xl1-protocol'
|
|
@@ -10,14 +9,11 @@ export const TransactionProtocolValidator: HydratedTransactionValidatorFunction<
|
|
|
10
9
|
): Promise<Error[]> => {
|
|
11
10
|
const errors: Error[] = []
|
|
12
11
|
try {
|
|
13
|
-
if (!await BoundWitnessWrapper.parse(tx).getValid()) {
|
|
14
|
-
errors.push(new Error('failed basic boundwitness parse'))
|
|
15
|
-
}
|
|
16
12
|
if (chainId !== undefined && tx.chain !== chainId) {
|
|
17
13
|
errors.push(new Error('invalid chain id'))
|
|
18
14
|
}
|
|
19
15
|
} catch (ex) {
|
|
20
16
|
errors.push(new Error('validation excepted'), ex as Error)
|
|
21
17
|
}
|
|
22
|
-
return errors
|
|
18
|
+
return await Promise.resolve(errors)
|
|
23
19
|
}
|