Skip to main content

Overview

Makes JSON-RPC calls with quorum consensus verification. The function cross-checks results from multiple nodes before returning, ensuring data consistency and reliability for critical operations. This is particularly useful for operations where data integrity is crucial, such as checking balances before transfers or verifying transaction status.

Function Signature

export const callWithQuorum = async <T = any>(
  method: string,
  params: any[] | object = [],
  quorum = 2
): Promise<T>

Parameters

method
string
required
The API method name (e.g., 'condenser_api.get_accounts', 'condenser_api.get_dynamic_global_properties')
params
any[] | object
default:"[]"
Parameters for the API method as an array or object. Most Hive API methods expect an array of parameters.
quorum
number
default:"2"
The number of nodes that must return the same result to reach consensus. Default is 2 (recommended minimum).Must be less than or equal to the total number of configured nodes.

Return Type

result
Promise<T>
Returns a Promise that resolves to the API response once consensus is reached. The response type can be specified using TypeScript generics.

How It Works

The quorum consensus mechanism:
  1. Random Node Selection: Nodes are randomly shuffled for better security
  2. Parallel Batch Calls: Makes parallel calls to quorum number of nodes
  3. Result Comparison: Compares results using JSON serialization
  4. Consensus Check: Returns when quorum nodes agree on the result
  5. Retry Logic: If consensus fails, tries the next batch of nodes
  6. Error on Failure: Throws error if all nodes are exhausted without reaching consensus

When to Use callWithQuorum

Use callWithQuorum for critical operations where data consistency is paramount:
  • Financial Operations: Checking balances before transfers
  • Transaction Verification: Confirming transaction inclusion
  • Critical State Queries: Getting account recovery status
  • Security-Sensitive Data: Verifying account authorities
For general queries and non-critical operations, use callRPC instead for better performance.

Examples

Basic Usage with Default Quorum

import { callWithQuorum } from 'hive-tx'

// Uses default quorum of 2 nodes
const accounts = await callWithQuorum(
  'condenser_api.get_accounts',
  [['alice']]
)
console.log(accounts)

Verify Account Balance Before Transfer

import { callWithQuorum } from 'hive-tx'

// Use quorum to ensure accurate balance
const accounts = await callWithQuorum(
  'condenser_api.get_accounts',
  [['alice']],
  3  // Require 3 nodes to agree
)

const balance = parseFloat(accounts[0].balance)
if (balance >= 10.000) {
  // Proceed with transfer
  console.log('Sufficient balance')
}

Check Transaction Confirmation

import { callWithQuorum } from 'hive-tx'

// Verify transaction was included in blockchain
const block = await callWithQuorum(
  'condenser_api.get_block',
  [blockNumber],
  2
)

const txFound = block.transactions.some(
  tx => tx.transaction_id === targetTxId
)
console.log('Transaction confirmed:', txFound)

Get Dynamic Global Properties with Consensus

import { callWithQuorum } from 'hive-tx'

const props = await callWithQuorum(
  'condenser_api.get_dynamic_global_properties',
  [],
  2
)
console.log('Current block:', props.head_block_number)
console.log('Block time:', props.time)

Higher Quorum for Critical Operations

import { callWithQuorum } from 'hive-tx'

// Require 4 nodes to agree for extra security
const account = await callWithQuorum(
  'condenser_api.get_accounts',
  [['critical-account']],
  4
)

// Verify account recovery status
const recoveryAccount = account[0].recovery_account
console.log('Recovery account:', recoveryAccount)

With TypeScript Type Safety

import { callWithQuorum } from 'hive-tx'

interface DynamicGlobalProperties {
  head_block_number: number
  time: string
  current_witness: string
  // ... other fields
}

const props = await callWithQuorum<DynamicGlobalProperties>(
  'condenser_api.get_dynamic_global_properties',
  [],
  2
)
// props is typed as DynamicGlobalProperties
console.log(props.head_block_number)

Error Handling

import { callWithQuorum } from 'hive-tx'

try {
  const result = await callWithQuorum(
    'condenser_api.get_accounts',
    [['alice']],
    3
  )
  console.log(result)
} catch (error) {
  if (error.message === "Couldn't reach quorum.") {
    console.error('Failed to get consensus from nodes')
  } else if (error.message === 'No more nodes available.') {
    console.error('All nodes failed to respond')
  } else if (error.message === 'quorum > config.nodes.length') {
    console.error('Not enough nodes configured for requested quorum')
  } else {
    console.error('Unexpected error:', error)
  }
}

Performance Considerations

  • Slower than callRPC: Makes multiple parallel calls to reach consensus
  • Network Overhead: Requires more bandwidth and API calls
  • Best for Critical Data: Use only when data consistency is crucial
  • Quorum Size: Higher quorum = more reliability but slower response

Comparison with callRPC

import { callRPC, callWithQuorum } from 'hive-tx'

// Fast but single node (potential inconsistency)
const quick = await callRPC('condenser_api.get_accounts', [['alice']])

// Slower but verified by multiple nodes (consistent)
const verified = await callWithQuorum('condenser_api.get_accounts', [['alice']], 2)

Behavior

  • Randomly shuffles nodes for better security distribution
  • Makes parallel calls to multiple nodes in batches
  • Compares results using JSON string comparison
  • Returns first result that reaches quorum threshold
  • Automatically retries with new nodes if consensus fails
  • Throws error if quorum exceeds available nodes
  • Each node gets one retry attempt on network failure

Configuration Requirements

import { config } from 'hive-tx'

// Ensure enough nodes are configured for your quorum
config.update({
  nodes: [
    'https://api.hive.blog',
    'https://api.hivekings.com',
    'https://anyx.io',
    'https://api.openhive.network',
    // Add more nodes for higher quorum values
  ]
})

// Now you can use quorum up to 4
const result = await callWithQuorum('method', params, 4)

See Also

Build docs developers (and LLMs) love