dagurval / electrum-cash-protocol

Electrum Cash protocol reference
MIT License
9 stars 6 forks source link

Improve 'blockchain.header.subscribe' notification documentation #21

Open dagurval opened 4 years ago

dagurval commented 4 years ago

The initial response and notification differ,

The inital response looks something like this

{"id":1073741824,"jsonrpc":"2.0","result":{"height":648871,"hex":"000000209309beb3a16731c9182fe34354c89451daf42e941310e80200000000000000008cfcc285727beec64d7299952a53a9e8cfd600b45d941a5d054bcaeb5318476d25b23b5fc8f8021885139c63"}}

While a notification looks something like this

{"jsonrpc":"2.0","method":"blockchain.headers.subscribe","params":[{"height":648875,"hex":"0000c0205c818c9b25da6ddf115b731257684350fedd0352adc879020000000000000000f3982d3a8acd68aef9f645cc9c6c4fda961dcd10cab647c0e68d668604c12e17d9b33b5ffdf202180a6a8912"}]}

This is not clear in the documentation.

Electron Cash handles this by "rewriting" the response https://github.com/Electron-Cash/Electron-Cash/blob/master/lib/network.py#L875

                # Rewrite response shape to match subscription request response
                method = response.get('method')
                params = response.get('params')
                k = self.get_index(method, params)
                if method == 'blockchain.headers.subscribe':
                    response['result'] = params[0]
                    response['params'] = []
                elif method == 'blockchain.scripthash.subscribe':
                    response['params'] = [params[0]]  # addr
                    response['result'] = params[1]
                callbacks = self.subscriptions.get(k, [])
monsterbitar commented 4 years ago

I have also found myself working around this in my code:

// If we received an array of exactly one block header..
if(Array.isArray(data) && data.length === 1)
{
    // Store the block hash and height.
    await this.setBlock(data[0].height, Buffer.from(data[0].hex, 'hex'));
}
// ..if we received a single block header..
else
{
    // Store the block hash and height.
    await this.setBlock(data.height, Buffer.from(data.hex, 'hex'));
}
dagurval commented 4 years ago

While we're sharing workarounds, here's mine in Kotlin 😂

            try {
                try {
                    // First response is wrapped in 'result', so use HeaderReply
                    val parsed = json.parse(HeaderReply.serializer(), it)
                    val header = BlockHeader.fromHex(parsed.result.hex)
                    header.height = parsed.result.height
                    callback(header)
                }
                catch (e: MissingFieldException) {
                    // Other responses are wrapped in params, use 'HeaderNotification'
                    val parsed = json.parse(HeaderNotification.serializer(), it)
                    val header = BlockHeader.fromHex(parsed.params.last().hex)
                    header.height = parsed.params.last().height
                    callback(header)
                }
            }
            catch (e: Exception) {
                LogIt.warning("Error when handling block header notification: $e")
            }