]> git.codecow.com Git - libnemo.git/commitdiff
Refactor account refresh to validate RPC response.
authorChris Duncan <chris@codecow.com>
Mon, 3 Aug 2026 06:42:24 +0000 (23:42 -0700)
committerChris Duncan <chris@codecow.com>
Mon, 3 Aug 2026 06:42:24 +0000 (23:42 -0700)
src/lib/account/refresh.ts

index c87e76725b6839a887b3f48ea28c4c0c272e22f3..98886740e00a2cbd0fbfd0fa3a32cbb2bb4cc449 100644 (file)
@@ -3,6 +3,7 @@
 
 import { Account } from '.'
 import { Block } from '../block'
+import { SCHEMA } from '../constants'
 import { Rpc } from '../rpc'
 
 export async function _refresh (account: Account, rpc: Rpc | string | URL): Promise<void>
@@ -21,16 +22,40 @@ export async function _refresh (account: Account, rpc: unknown): Promise<void> {
                weight: true
        }
        const resAccountInfo = await rpc.post('account_info', reqAccountInfo)
-       if (resAccountInfo.frontier == null) {
+       if (resAccountInfo == null || typeof resAccountInfo !== 'object') {
+               throw new TypeError('Invalid account_info response')
+       }
+       const accountInfo = resAccountInfo as Record<string, unknown>
+       if (accountInfo.frontier == null) {
                throw new Error('Account not found')
        }
-       Object.assign(account, resAccountInfo)
+       for (const [k, v] of Object.entries(accountInfo)) {
+               if (SCHEMA.account_info.fields.includes(k) && typeof v === 'string') {
+                       account[k] = v
+               }
+       }
 
-       const reqConfirmedFrontier = {
+       const reqConfirmedFrontierBlockInfo = {
                json_block: true,
                hash: account.confirmed_frontier
        }
-       const { contents: confirmedFrontierContents, subtype: confirmedFrontierSubtype } = await rpc.post('block_info', reqConfirmedFrontier)
+       const resConfirmedFrontierBlockInfo = await rpc.post('block_info', reqConfirmedFrontierBlockInfo)
+       if (resConfirmedFrontierBlockInfo == null || typeof resConfirmedFrontierBlockInfo !== 'object') {
+               throw new TypeError('Invalid confirmed frontier block_info response')
+       }
+       if (!('subtype' in resConfirmedFrontierBlockInfo) || typeof resConfirmedFrontierBlockInfo.subtype !== 'string') {
+               throw new Error('Confirmed frontier block info subtype not found')
+       }
+       const confirmedFrontierSubtype = resConfirmedFrontierBlockInfo.subtype
+       if (!('contents' in resConfirmedFrontierBlockInfo) || resConfirmedFrontierBlockInfo.contents == null || !(typeof resConfirmedFrontierBlockInfo.contents !== 'object')) {
+               throw new Error('Confirmed frontier block info contents not found')
+       }
+       const confirmedFrontierContents: Record<string, string> = {}
+       for (const [k, v] of Object.entries(resConfirmedFrontierBlockInfo.contents)) {
+               if (SCHEMA.block_info.contents.fields.includes(k) && typeof v === 'string') {
+                       confirmedFrontierContents[k] = v
+               }
+       }
        const confirmedFrontierBlock = new Block(confirmedFrontierContents.account, confirmedFrontierContents.balance, confirmedFrontierContents.previous, confirmedFrontierContents.representative)
        if (typeof confirmedFrontierBlock[confirmedFrontierSubtype] !== 'function') {
                throw new TypeError('Unknown subtype of confirmed frontier block', { cause: confirmedFrontierSubtype })
@@ -38,11 +63,27 @@ export async function _refresh (account: Account, rpc: unknown): Promise<void> {
        confirmedFrontierBlock[confirmedFrontierSubtype](confirmedFrontierContents.link, 0).signature = confirmedFrontierContents.signature
        account.confirmed_frontier_block = confirmedFrontierBlock
 
-       const reqFrontier = {
+       const reqFrontierBlockInfo = {
                json_block: true,
                hash: account.frontier
        }
-       const { contents: frontierContents, subtype: frontierSubtype } = await rpc.post('block_info', reqFrontier)
+       const resFrontierBlockInfo = await rpc.post('block_info', reqFrontierBlockInfo)
+       if (resFrontierBlockInfo == null || typeof resFrontierBlockInfo !== 'object') {
+               throw new TypeError('Invalid frontier block_info response')
+       }
+       if (!('subtype' in resFrontierBlockInfo) || typeof resFrontierBlockInfo.subtype !== 'string') {
+               throw new Error('Frontier block info subtype not found')
+       }
+       const frontierSubtype = resFrontierBlockInfo.subtype
+       if (!('contents' in resFrontierBlockInfo) || resFrontierBlockInfo.contents == null || !(typeof resFrontierBlockInfo.contents !== 'object')) {
+               throw new Error('Frontier block info contents not found')
+       }
+       const frontierContents: Record<string, string> = {}
+       for (const [k, v] of Object.entries(resFrontierBlockInfo.contents)) {
+               if (SCHEMA.block_info.contents.fields.includes(k) && typeof v === 'string') {
+                       frontierContents[k] = v
+               }
+       }
        const frontierBlock = new Block(frontierContents.account, frontierContents.balance, frontierContents.previous, frontierContents.representative)
        if (typeof frontierBlock[frontierSubtype] !== 'function') {
                throw new TypeError('Unknown subtype of frontier block', { cause: frontierSubtype })