事件和套接字

要监听hsd中的事件并做出响应,必须在运行时脚本中添加监听器。 例如,如果您运行的是完整节点,则可能已经熟悉了hsd节点启动脚本, 该脚本实例化 FullNode对象,添加钱包并开始连接和同步。 该脚本以实际启动这些过程的异步函数结尾,这是添加事件侦听器的好地方。使用下表的事件目录, 您可以发现每种事件类型需要侦听哪个对象。注意,因为钱包是作为插件添加的,所以它的对象层次结构路径有点……笨拙:-)

// Based on https://github.com/handshake-org/hsd/blob/master/bin/node
(async () => {
await node.ensure();
await node.open();
await node.connect();
node.startSync();
// add event listeners after everything is open, connected, and started
// NODE
node.on('tx', (details) => {
console.log(' -- node tx -- \n', details)
});
node.on('block', (details) => {
console.log(' -- node block -- \n', details)
});
// MEMPOOL
node.mempool.on('confirmed', (details) => {
console.log(' -- mempool confirmed -- \n', details)
});
// WALLET
node.plugins.walletdb.wdb.on('balance', (details) => {
console.log(' -- wallet balance -- \n', details)
});
node.plugins.walletdb.wdb.on('confirmed', (details) => {
console.log(' -- wallet confirmed -- \n', details)
});
node.plugins.walletdb.wdb.on('address', (details) => {
console.log(' -- wallet address -- \n', details)
});
})().catch((err) => {
console.error(err.stack);
process.exit(1);
});

事件目录

该列表对于涵盖所有Handshake交易,钱包和区块链活动。 为了简单起见,已省略了有关错误,套接字连接和对等连接的事件。 请注意,某些方法发出相同的事件,但返回对象不同,并不是所有的所有重新发射器都返回它们接收的所有内容。

EventReturnsOriginRe-Emitters
tipChainEntryChain: open(), disconnect(), reconnect(), setBestChain(), reset()
connectChainEntry, Block, CoinViewChain: setBestChain(), reconnect()chainFullNode (returns ChainEntry, Block only)
chainSPVNode (returns ChainEntry, Block only)
SPVNode, FullNodeNodeClient (emits as block connect, returns ChainEntry, Block.txs only)
disconnectChainEntry, Headers, CoinViewChain: reorganizeSPV()chainFullNode (returns ChainEntry, Headers only)
chainSPVNode (returns ChainEntry, Headers only)
SPVNode, FullNodeNodeClient (emits as block disconnect, returns ChainEntry only)
disconnectChainEntry, Block, CoinViewChain: disconnect()chainFullNode (returns ChainEntry, Block only)
chainSPVNode (returns ChainEntry, Block only)
SPVNode, FullNodeNodeClient (emits as block disconnect, returns ChainEntry only)
reconnectChainEntry, BlockChain: reconnect()
reorganizetip (ChainEntry), competitor (ChainEntry)Chain: reorganize(), reorganizeSPV()chainFullNode
chainSPVNode
blockBlock, ChainEntryChain: setBestChain()
CPUMiner: _start()
chainPool
chainFullNode (returns Block only)
chainSPVNode (returns Block only)
competitorBlock, ChainEntryChain: saveAlternate()
bad orphanError, IDChain: handleOrphans()
Mempool: handleOrphans()
resolvedBlock, ChainEntryChain: handleOrphans()
checkpointHash, HeightChain: verifyCheckpoint()
orphanBlockChain: storeOrphan()
fullChain: maybeSync()chainPool
confirmedTX, ChainEntryMempool: _addBlock()
confirmedTX, DetailsTXDB: confirm()txdbWalletDB (also returns Wallet)
txdbWallet
unconfirmedTX, BlockMempool: _removeBlock()
unconfirmedTX, DetailsTXDB: disconnect()txdbWalletDB (also returns Wallet)
txdbWallet
conflictTXMempool: _removeBlock()
conflictTX, DetailsTXDB: disconnect()txdbWalletDB (also returns Wallet)
txdbWallet
txTX, CoinViewMempool: addEntry()mempoolPool (returns TX only)
mempoolpoolSPVNode (returns TX only)
mempoolFullNode (returns TX only)
SPVNode, FullNodeNodeClient (returns TX only)
txTXPool: _handleTX()
(only if there is no mempool, i.e. SPV)
poolSPVNode
txTX, DetailsTXDB: insert()txdbWalletDB (also returns Wallet)
txdbWallet
double spendMempoolEntryMempool: removeDoubleSpends()
balanceBalanceTXDB: insert(), confirm(), disconnect(), erase()txdbWalletDB (also returns Wallet)
txdbWallet
address[WalletKey]Wallet: _add(),walletWalletDB (returns parent Wallet, [WalletKey])

Socket事件

hsd中的Websocket连接由两个服务处理,一个用于Node,一个用于Wallet。 每个服务都有子对象,如ChainMempoolPoolWalletDB,并从它们的套接字中中继事件。 为了接收事件,套接字客户端必须监听一个频道(例如chainmempoolauth),或加入一个钱包(用户定义为primaryhot-walletmultisig1)。 所有钱包都可以通过'*'一次连接起来。

用bsock监听Socket事件

要建立与hsd的套接字连接,您需要运行一个websocket客户端。 幸运的是,bcoin和hsd开发人员已经开发了bsock,这是socket.io协议的一种仅基于Websocket的最小实现。 默认情况下,bsock监听localhost,并且您只需要向其传递一个端口号即可连接到其中一台hsd服务器。 下面的示例说明了如何建立套接字连接,如何使用用户定义的API密钥进行身份验证以及如何发送和接收事件! 有关hsd中可用的调用和事件的完整列表,请参见下面的套接字事件目录

// bsock-example.js
const bsock = require('bsock');
const {Network, ChainEntry} = require('hsd');
const network = Network.get('regtest');
const apiKey = 'api-key';
nodeSocket = bsock.connect(network.rpcPort);
walletSocket = bsock.connect(network.walletPort);
nodeSocket.on('connect', async (e) => {
try {
console.log('Node - Connect event:\n', e);
// `auth` must be called before any other actions
console.log('Node - Attempting auth:\n', await nodeSocket.call('auth', apiKey));
// `watch chain` subscirbes us to chain events like `block`
console.log('Node - Attempting watch chain:\n', await nodeSocket.call('watch chain'));
// Some calls simply request information from the server like an http request
console.log('Node - Attempting get tip:');
const tip = await nodeSocket.call('get tip');
console.log(ChainEntry.from(tip));
} catch (e) {
console.log('Node - Connection Error:\n', e);
}
});
// listen for new blocks
nodeSocket.bind('chain connect', (raw, txs) => {
console.log('Node - Chain Connect Event:\n', ChainEntry.fromRaw(raw));
});
walletSocket.on('connect', async (e) => {
try {
console.log('Wallet - Connect event:\n', e);
// `auth` is required before proceeding
console.log('Wallet - Attempting auth:\n', await walletSocket.call('auth', apiKey));
// here we join all wallets, but we could also just join `primary` or any other wallet
console.log('Wallet - Attempting join *:\n', await walletSocket.call('join', '*'));
} catch (e) {
console.log('Wallet - Connection Error:\n', e);
}
});
// listen for new wallet transactions
walletSocket.bind('tx', (wallet, tx) => {
console.log('Wallet - TX Event -- wallet:\n', wallet);
console.log('Wallet - TX Event -- tx:\n', tx);
});
// listen for new address events
// (only fired when current account address receives a transaction)
walletSocket.bind('address', (wallet, json) => {
console.log('Wallet - Address Event -- wallet:\n', wallet);
console.log('Wallet - Address Event -- json:\n', json);
});

要查看此脚本的运行情况,请首先启动hsd:

hsd --daemon --network=regtest

运行脚本(这是事件输出的打印位置):

node bsock-example.js

然后,在单独的终端窗口中,运行一些命令来触发事件!

hsd-cli rpc generatetoaddress 1 rs1qyep4stuujjkdfv483nfse53vvgtfuqr9z6v4cm

更易用的Sockets: hs-client

bsock是用于处理套接字的出色的底层库,但是我们还有hs-client,它可以简化套接字和常规的http连接。 终端中的一个单行命令可以侦听所有钱包事件并打印所有返回的数据:

hsw-cli listen

hs-client也可以在脚本中用于侦听事件。如果您已经熟悉了hsd-cli API,将轻车熟路。这是上面脚本的一部分,使用hs-client来代替bsock:

const {NodeClient} = require('hs-client');
const {Network, ChainEntry} = require('hsd');
const network = Network.get('regtest');
const clientOptions = {
network: network.type,
port: network.rpcPort,
apiKey: 'api-key'
}
const client = new NodeClient(clientOptions);
(async () => {
// bclient handles the connection, the auth, and the channel subscriptions
await client.open();
// use socket connection to request data
const tip = await client.getTip();
console.log(tip);
})();
// listen for new blocks
client.bind('chain connect', (raw) => {
console.log('Node - Chain Connect Event:\n', ChainEntry.fromRaw(raw));
});

套接字事件目录

钱包

所有钱包事件均由 WalletDB对象广播,可由其父对象 TXDBWallet 触发。 套接字发出事件以及钱包ID以及上面列出的相同“ Returns”。

EventReturns
txWalletID, TX Details
confirmedWalletID, TX Details
unconfirmedWalletID, TX Details
conflictWalletID, TX Details
balanceWalletID, Balance
addressWalletID, [WalletKey]

节点

EventReturnsChannelOriginOriginal Event
chain connectChainEntry.toRaw()chainChainconnect
block connectChainEntry.toRaw(), Block.txschainChainconnect
chain disconnectChainEntry.toRaw()chainChaindisconnect
block disconnectChainEntry.toRaw()chainChaindisconnect
tx TX.toRaw()mempoolPooltx

服务器勾子

某些事件也可以从客户端发送回服务器,以请求新数据或触发服务器操作。客户端动作是一个“调用”,而服务器等待着一个“勾子”。

钱包

EventArgsReturns
auth1. api keynull
join1. wallet id
2. wallet token
null
leave1. wallet idnull

节点

EventArgsReturns
auth1. api keynull
watch chain(none)null
unwatch chain(none)null
watch mempool(none)null
unwatch mempool(none)null
set filter1. Bloom filter (Buffer)null
get tip(none)ChainEntry.toRaw()
get entry1. hashChainEntry.toRaw()
get hashes1. start (int)
2. end (int)
[hashes]
add filter1. filter ([Buffer])null
reset filter(none)null
estimate fee1. blocks (int)fee rate (float)
send1. tx (Buffer)null
rescan1. hashnull