Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add block height in relevant adapters #215

Merged
merged 23 commits into from
Jan 15, 2021

Conversation

ebarakos
Copy link
Contributor

@ebarakos ebarakos commented Jan 8, 2021

No description provided.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2021

I see that you haven't updated any CHANGELOG files. Would it make sense to do so?

@ebarakos ebarakos force-pushed the feature/176305964-block-height-adapters branch from 0255ba3 to 44572aa Compare January 8, 2021 12:51
@ebarakos
Copy link
Contributor Author

ebarakos commented Jan 8, 2021

Currently, the common interface between all 4(3 APIs + json-rpc) adapters that return bitcoin info is: { "blockchain": "BTC", q: "difficulty"} & { "blockchain": "BTC", q: "height"}.

I didn't add many validations on q, so it can potentially support other parameters as well.
Tried to make it backwards compatible, in case previous adapter params are already used by node operators. (in cryptoapis you can use endpoint instead of q and q param defaults to difficulty in other adapters).

We can improve the uniform interface/naming for blockchain info/stats, either now or in the future and choose a convention either a) created by us b) identical to the getblockchainfo params naming c) one of external APIs, like cryptoapis

@ebarakos ebarakos marked this pull request as ready for review January 8, 2021 12:54
Copy link
Contributor

@krebernisak krebernisak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @ebarakos!

I tried to leave some comments and ideas, but we should probably also sync offline on this.

In general:

  1. Please use the existing endpoint input param instead of a new one q (take a look at existing adapters using endpoint on input)
  2. Don't add custom logic to our general json-rpc adapter, but you can make a new bitcoin-json-rpc adapter, that wraps/uses the underlying json-rpc but follows our standard API.
  3. In the future we should rename our endpoint input param as dataPoint and codify all data point types that we support in TS + docs

blockchair/adapter.js Outdated Show resolved Hide resolved
cryptoapis/adapter.js Outdated Show resolved Hide resolved
cryptoapis/adapter.js Outdated Show resolved Hide resolved
json-rpc/src/adapter.ts Outdated Show resolved Hide resolved
cryptoapis/adapter.js Outdated Show resolved Hide resolved
const execute = (input, callback) => {
const validator = new Validator(input, customParams)
if (validator.error) return callback(validator.error.statusCode, validator.errored)

const jobRunID = validator.validated.id
let blockchain = validator.validated.data.blockchain
let q = validator.validated.data.q || 'difficulty'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently use an endpoint input param to differentiate between different data points we want to fetch from the external data provider.

I would suggest we don't introduce new behavior to the adapter API at this point but continue using what we have now.

Take a look at how we handle endpoint queries in different providers, and please try to mimic the interface and behavior here.


In the future, we are looking to streamline the adapter API. The first thing we should do is list the data points we support and codify them. Here you are adding support for a new data point, let's call it BlockNumber. All of our adapters that know how to answer requests for that specific data point should follow the same API (input/output).

In this context, it may make sense to wrap the json-rpc adapter into bitcoin-json-rpc adapter that will then expose this specific data point (and others).

network: false,
}

const difficulty = (jobRunID, input, callback) => {
const validator = new Validator(input, difficultyParams)
const latestBlock = (jobRunID, input, callback) => {
Copy link
Contributor

@krebernisak krebernisak Jan 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how you renamed this as latestBlock, as this actually is the latestBlock endpoint from the cryptoapis data provider. We also call our input param endpoint, but IMHO this is the wrong name for this input param.

What our input param should be called is a dataPoint, which we then map to 1 (or more) underlying API endpoints from which we fetch data.

Here the cryptoapis adapter supports two data points (we call them endpoint) and for both of those (difficulty|height) we fetch data from the same underlying HTTP endpoint (latestBlock).

@ebarakos
Copy link
Contributor Author

ebarakos commented Jan 8, 2021

Thank you @krebernisak for your helpful review.

I will revert q to endpoint, along with the other changes, and we can tackle this in the future, across the whole codebase, as you mentioned.

@ebarakos ebarakos changed the title Added height(latest block number) as endpoint parameter on cryptoapis Add block height in relevant adapters Jan 8, 2021
@ebarakos
Copy link
Contributor Author

Created bitcoin-json-rpc as composite adapter. It can be used with the same parameters as json-rpc, but also adheres to the { "blockchain": "BTC", endpoint: "difficulty"} & { "blockchain": "BTC", endpoint: "height"} convention of other relevant adapters (blockchain param can be omitted).

@ebarakos ebarakos force-pushed the feature/176305964-block-height-adapters branch 8 times, most recently from 0042ba6 to 8ddf9f3 Compare January 14, 2021 15:14
Comment on lines 7 to 9
url: false,
method: false,
params: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url, method and params will be handled by JSONRPC adapter. You can just forward the input to the json adapter an it will handle it

Copy link
Contributor Author

@ebarakos ebarakos Jan 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just left them here, so this composite adapter can inherit the actual functionality of the json-rpc one.

I reverted it now to a simpler version, that just accepts the endpoint param, for querying for blockchain stats, according to the scope of the ticket.

if (endpoint != undefined || blockchain != undefined) {
if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint]
if (!endpoint) endpoint = 'difficulty'
request.data.method = 'getblockchaininfo'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want method only to be 'getblockchaininfo' only if we don't have endpoint or blockchain params? Default values could come from makeConfig better.
Also, let's try to not modify directly the request object.

const blockchain = validator.validated.data.blockchain.toLowerCase()

const key = util.getRandomRequiredEnv('API_KEY')
const q = endpointToQ[endpoint]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is q? Could it have a more explanatory name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q is a param of the cryptoid endpoint.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would give the var an explicit name, even if it will be a param of the request, but we'll know what is it.

const someExplicitName = endpointToXXX[endpoint]
const params = { key, q: someExplicitName }

import { makeExecute } from './adapter'
import { makeConfig } from './config'

const NAME = 'EXAMPLE'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you forgot to change the NAME

@ebarakos ebarakos force-pushed the feature/176305964-block-height-adapters branch from 8ddf9f3 to edcff2a Compare January 15, 2021 08:57
Copy link
Member

@RodrigoAD RodrigoAD left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ebarakos for the changes! Looking great, just some final notes

const blockchain = validator.validated.data.blockchain.toLowerCase()

const key = util.getRandomRequiredEnv('API_KEY')
const q = endpointToQ[endpoint]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would give the var an explicit name, even if it will be a param of the request, but we'll know what is it.

const someExplicitName = endpointToXXX[endpoint]
const params = { key, q: someExplicitName }

composite/bitcoin-json-rpc/src/adapter.ts Outdated Show resolved Hide resolved
composite/bitcoin-json-rpc/README.md Show resolved Hide resolved
composite/bitcoin-json-rpc/README.md Outdated Show resolved Hide resolved
@RodrigoAD
Copy link
Member

Take a look at the failing tests of bitcoin-rpc adapter, the build is trying to fetch a local node

const blockchain = validator.validated.data.blockchain.toLowerCase()

const key = util.getRandomRequiredEnv('API_KEY')
const apiParam = endpointToApiParam[endpoint]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for being picky here, as I understand q param is the function you want to call from cryptoId, so something like cryptoIdFunction or apiFunction would be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q is the query parameter of the cryptoid endpoint. Not sure if it should be called function. In essence though, it has identical name as the JSON RPC methods.

I was thinking of something to let us know that the endpoint is mapped to the actual api's parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to apiFunctionName after discussion.

@ebarakos ebarakos force-pushed the feature/176305964-block-height-adapters branch from 6b06009 to a62df92 Compare January 15, 2021 11:01
blockchair/src/endpoint/balance.ts Outdated Show resolved Hide resolved
composite/bitcoin-json-rpc/README.md Outdated Show resolved Hide resolved
composite/bitcoin-json-rpc/src/adapter.ts Show resolved Hide resolved
composite/bitcoin-json-rpc/src/adapter.ts Outdated Show resolved Hide resolved
composite/bitcoin-json-rpc/src/index.ts Outdated Show resolved Hide resolved
composite/bitcoin-json-rpc/src/config.ts Outdated Show resolved Hide resolved

export const Name = 'difficulty'
export const Name = 'stats'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we name this as "stats" when we are actually calling Crypoapis /bc/.../info endpoint?

We should be calling it what it is, let's say blockchain-info or bc-info.

For Blockchair the endpoint is "stats", and we should call it "stats", but it's different for this adapter. When we get a query for BTC height, for Crypoapis adapter we need to call their bc-info endpoint and read the payload.blocks path from the response..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of adhering to a certain name for this category of calls. Like we have balance and price, which also do not correspond to the actual endpoint naming.

Copy link
Contributor

@krebernisak krebernisak Jan 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well just because we have some code implemented currently does not make it the correct way to do it :)

I suggest you rename this for now, and we'll make sure to improve/refactor other adapter implementations in the future.

"balance", "price", "difficulty" & "height" are all data points for which one can query our system. There is no "stats" data point currently. Here you are mapping the user request for "height" data point to a cryptoapis "bc-info" endpoint request.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to bc_info for now.

I think we have to find a uniform convention for these adapters that are returning blockchain info, while keeping difficulty (and now height) in backwards compatibility(probably by just delegating to stats/bc_info types when seeing these types of endpoint).

blockchain: ['blockchain', 'coin'],
endpoint: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not reuse the name "endpoint" in our internal API. It just does not fit, just as it does not fit in the external API, but we are currently stuck with it there because of backward compatibility.

Internally, we should structure this differently.

So let's take this adapter and endpoint as an example, the "stats" endpoint.
The stats endpoint impl should have no knowledge of a concept like "height", this is a concept of our external API, it has nothing to do with "stats" endpoint. We should map a CL "height" request to a Cryptoapis "stats" request, but the impl for Cryptoapis "stats" request should have no knowledge of the outside CL "height" request.

The input to "stats" endpoint should look something like this:

const inputParams = {
  blockchain: true,
  network: false,
  path: false  
}

It makes a "stats" requests, and returns the data point at "path". If the "path" is empty, it returns the whole response body.

The component that is calling this should map from the CL API request to the "stats" API request. So your adapter in the top lvl execute function gets the input:

{
  "blockchain": "btc",
  "endpoint": height
}

We should map that to the underlying API endpoint fn call like this:

{
  "blockchain": "bitcoin",
  "network": "mainnet",
  "path": "payload.blocks"
}

@ebarakos @RodrigoAD @justinkaseman @emmick4 please acknowledge & discuss!

As this is a high priority for today, you can ignore this for this specific PR, but keep it in mind on the next one. I am also going to open an issue for it, to improve our older implementations.

cryptoid/src/adapter.ts Outdated Show resolved Hide resolved
cryptoid/src/adapter.ts Show resolved Hide resolved
cryptoid/src/config.ts Show resolved Hide resolved
composite/bitcoin-json-rpc/src/adapter.ts Outdated Show resolved Hide resolved
cryptoapis/src/endpoint/bc_info.ts Outdated Show resolved Hide resolved
cryptoid/src/adapter.ts Outdated Show resolved Hide resolved
cryptoapis/test/stats.test.ts Outdated Show resolved Hide resolved
blockchair/src/endpoint/stats.ts Outdated Show resolved Hide resolved
cryptoapis/README.md Outdated Show resolved Hide resolved
cryptoid/README.md Outdated Show resolved Hide resolved
cryptoapis/README.md Outdated Show resolved Hide resolved
Copy link
Contributor

@krebernisak krebernisak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be good to go!

A lot of valuable discussions in this PR 🙌 Thanks, @ebarakos 👍

@ebarakos
Copy link
Contributor Author

Thanks for the thorough review @krebernisak and @RodrigoAD!

@ebarakos ebarakos merged commit e5aca08 into develop Jan 15, 2021
@ebarakos ebarakos deleted the feature/176305964-block-height-adapters branch January 15, 2021 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants