]> git.codecow.com Git - libnemo.git/commitdiff
Implement more RPC response validators.
authorChris Duncan <chris@codecow.com>
Fri, 7 Aug 2026 06:37:24 +0000 (23:37 -0700)
committerChris Duncan <chris@codecow.com>
Fri, 7 Aug 2026 06:37:24 +0000 (23:37 -0700)
src/lib/rpc/accounts_frontiers.ts
src/lib/rpc/block_info.ts
src/lib/rpc/blocks_info.ts
src/lib/rpc/schema.ts
src/lib/wallet/refresh.ts

index 90ac81f09f0ffbdeb526077813f5a2d4abf92783..dce1ee06899e8d61c129c291f461dbeea3831d77 100644 (file)
@@ -1,19 +1,60 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
+//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { Account } from '../account'
+import { hex } from '../convert'
+
+type AccountsFrontiers = {
+       frontiers?: {
+               [address: string]: string
+       }
+       errors?: {
+               [address: string]: string
+       }
+}
+
+export function accounts_frontiers (body: unknown): AccountsFrontiers {
+       const response: AccountsFrontiers = {}
+       if (body == null || typeof body !== 'object') {
+               return response
+       }
+       const frontiers = 'frontiers' in body ? body.frontiers : null
+       const errors = 'errors' in body ? body.errors : null
+
+       if (frontiers != null) {
+               if (typeof frontiers === 'object') {
+                       for (const [address, frontier] of Object.entries(frontiers) as [string, unknown][]) {
+                               try {
+                                       Account.validate(address)
+                                       if (hex.is(frontier)) {
+                                               response.frontiers ??= {}
+                                               response.frontiers[address] = frontier
+                                       }
+                               } catch (err: any) {
+                                       response.errors ??= {}
+                                       response.errors[address] = err?.message
+                               }
+                       }
+               }
+       }
+
+       if (errors != null) {
+               if (typeof errors === 'object') {
+                       for (const [address, error] of Object.entries(errors) as [string, unknown][]) {
+                               try {
+                                       Account.validate(address)
+                                       if (typeof error === 'string') {
+                                               throw new Error(error)
+                                       }
+                               } catch (err: any) {
+                                       response.errors ??= {}
+                                       response.errors[address] = err?.message
+                               }
+                       }
+               }
+       }
 
-export const accounts_frontiers = Object.freeze({
-       fields: [
-               'errors',
-               'frontiers',
-       ],
-       errors: {
-               address: {
-                       error: {},
-               },
-       },
-       frontiers: {
-               address: {
-                       hash: {},
-               },
-       },
-})
+       return response
+}
index fc2ec56312f88b5bcd75a420a6ddf5a538b40c62..da3bf7f40b9c9921e30d960a4c708c5ba927fa9b 100644 (file)
@@ -1,30 +1,75 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-export const block_info = Object.freeze({
-       fields: [
-               'amount',
-               'balance',
-               'block_account',
-               'confirmed',
-               'contents',
-               'height',
-               'linked_account',
-               'local_timestamp',
-               'subtype',
-               'successor',
-       ],
+
+export type BlockInfo = {
        contents: {
-               fields: [
-                       'account',
-                       'balance',
-                       'link',
-                       'link_as_account',
-                       'previous',
-                       'representative',
-                       'signature',
-                       'type',
-                       'work',
-               ],
-       },
-})
+               account: string
+               balance: string
+               link: string
+               link_as_account: string
+               previous: string
+               representative: string
+               signature: string
+               type: string
+               work: string
+       }
+       amount: string
+       balance: string
+       block_account: string
+       confirmed: string
+       height: string
+       linked_account: string
+       local_timestamp: string
+       subtype: string
+       successor: string
+}
+
+export function block_info (body: unknown): BlockInfo {
+       if (body != null && typeof body === 'object'
+               && 'amount' in body && typeof body.amount === 'string'
+               && 'balance' in body && typeof body.balance === 'string'
+               && 'block_account' in body && typeof body.block_account === 'string'
+               && 'confirmed' in body && typeof body.confirmed === 'string'
+               && 'height' in body && typeof body.height === 'string'
+               && 'linked_account' in body && typeof body.linked_account === 'string'
+               && 'local_timestamp' in body && typeof body.local_timestamp === 'string'
+               && 'subtype' in body && typeof body.subtype === 'string'
+               && 'successor' in body && typeof body.successor === 'string'
+               && 'contents' in body && body.contents != null && typeof body.contents === 'object'
+               && 'account' in body.contents && typeof body.contents.account === 'string'
+               && 'balance' in body.contents && typeof body.contents.balance === 'string'
+               && 'link' in body.contents && typeof body.contents.link === 'string'
+               && 'link_as_account' in body.contents && typeof body.contents.link_as_account === 'string'
+               && 'previous' in body.contents && typeof body.contents.previous === 'string'
+               && 'representative' in body.contents && typeof body.contents.representative === 'string'
+               && 'signature' in body.contents && typeof body.contents.signature === 'string'
+               && 'type' in body.contents && typeof body.contents.type === 'string'
+               && 'work' in body.contents && typeof body.contents.work === 'string'
+       ) {
+               return {
+                       contents: {
+                               account: body.contents.account,
+                               balance: body.contents.balance,
+                               link: body.contents.link,
+                               link_as_account: body.contents.link_as_account,
+                               previous: body.contents.previous,
+                               representative: body.contents.representative,
+                               signature: body.contents.signature,
+                               type: body.contents.type,
+                               work: body.contents.work,
+                       },
+                       amount: body.amount,
+                       balance: body.balance,
+                       block_account: body.block_account,
+                       confirmed: body.confirmed,
+                       height: body.height,
+                       linked_account: body.linked_account,
+                       local_timestamp: body.local_timestamp,
+                       subtype: body.subtype,
+                       successor: body.successor,
+               }
+       } else {
+               throw new Error('Invalid block_info body')
+       }
+}
index 532dc27836e49218d72ffbf57460cad65f9c7de7..16159960e4c9eb2ebefe8db4bb10508f4fd6ad21 100644 (file)
@@ -1,37 +1,61 @@
+
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-export const blocks_info = Object.freeze({
-       fields: [
-               'blocks',
-       ],
-       blocks: {
-               hash: {
-                       fields: [
-                               'amount',
-                               'balance',
-                               'block_account',
-                               'confirmed',
-                               'contents',
-                               'height',
-                               'linked_account',
-                               'local_timestamp',
-                               'subtype',
-                               'successor',
-                       ],
-                       contents: {
-                               fields: [
-                                       'account',
-                                       'balance',
-                                       'link',
-                                       'link_as_account',
-                                       'previous',
-                                       'representative',
-                                       'signature',
-                                       'type',
-                                       'work',
-                               ],
-                       },
-               },
+import { hex } from '../convert'
+import { BlockInfo, block_info } from './block_info'
+
+type BlocksInfo = {
+       blocks?: {
+               [hash: string]: BlockInfo
        },
-})
+       errors?: {
+               [address: string]: string
+       },
+}
+
+export function blocks_info (body: unknown): BlocksInfo {
+       const response: BlocksInfo = {}
+       if (body == null || typeof body !== 'object') {
+               return response
+       }
+       const blocks = 'blocks' in body ? body.blocks : null
+       const errors = 'errors' in body ? body.errors : null
+
+       if (blocks != null) {
+               if (typeof blocks === 'object') {
+                       for (const [hash, data] of Object.entries(blocks) as [string, unknown][]) {
+                               try {
+                                       if (hex.is(hash)) {
+                                               response.blocks ??= {}
+                                               response.blocks[hash] = block_info(data)
+                                       } else {
+                                               throw new Error('Invalid blocks_info hash')
+                                       }
+                               } catch (err: any) {
+                                       response.errors ??= {}
+                                       response.errors[hash] = err?.message
+                               }
+                       }
+               }
+       }
+
+       if (errors != null) {
+               if (typeof errors === 'object') {
+                       for (const [hash, error] of Object.entries(errors) as [string, unknown][]) {
+                               try {
+                                       if (hex.is(hash) && typeof error === 'string') {
+                                               throw new Error(error)
+                                       } else {
+                                               throw new Error('Invalid blocks_info error')
+                                       }
+                               } catch (err: any) {
+                                       response.errors ??= {}
+                                       response.errors[hash] = err?.message
+                               }
+                       }
+               }
+       }
+
+       return response
+}
index 6d4c444d8a2615786634dcbfe7505b6a86f84458..bb3a7ce7eef6b94ced117ddc8178e0cecc49fc34 100644 (file)
@@ -1,5 +1,6 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
+import { account_balance } from './account_balance'
 import { account_info } from './account_info'
 import { account_representative } from './account_representative'
 import { accounts_balances } from './accounts_balances'
@@ -10,12 +11,13 @@ import { blocks_info } from './blocks_info'
 import { process } from './process'
 
 export const Schema = Object.freeze({
+       account_balance,
        // account_info,
        // account_representative,
        accounts_balances,
-       // accounts_frontiers,
+       accounts_frontiers,
        // accounts_representatives,
-       // block_info,
-       // blocks_info,
+       block_info,
+       blocks_info,
        // process,
 })
index 4ef3d43efa458aebd6b9de019ff6ae71748cf289..eb699ee9f870d7e9abb7e545ceeaa82ba4e0f5ee 100644 (file)
@@ -6,18 +6,6 @@ import { Block } from '../block'
 import { Rpc, Schema } from '../rpc'
 import { Wallet } from '../wallet'
 
-export type BlockInfo = {
-       blocks: {
-               [hash: string]: {
-                       [p: string]: string
-               } & {
-                       contents: {
-                               [p: string]: string
-                       }
-               }
-       }
-}
-
 export async function _refresh (wallet: Wallet, rpc: Rpc | string | URL, from: number, to: number): Promise<Map<number, Account>>
 export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to: unknown): Promise<Map<number, Account>> {
        try {
@@ -41,8 +29,8 @@ export async function _refresh (wallet: Wallet, rpc: unknown, from: unknown, to:
                        accounts: addresses
                }
                const { balances } = await rpc.post('accounts_balances', data)
-               const { frontiers } = await rpc.post('accounts_frontiers', data) as { frontiers: { [address: string]: string } }
-               const { blocks } = await rpc.post('blocks_info', { json_block: true, hashes: Object.values(frontiers) }) as BlockInfo
+               const { frontiers } = await rpc.post('accounts_frontiers', data)
+               const { blocks } = await rpc.post('blocks_info', { json_block: true, include_not_found: true, hashes: Object.values(frontiers) }) as BlockInfo
                for (const account of accounts.values()) {
                        account.balance = balances?.[account.address]?.balance ?? 0
                        account.receivable = balances?.[account.address]?.receivable ?? 0