Skip to content

This Orchestrate library provides convenient access to the Orchestrate API from applications written in server-side NodeJS

License

Notifications You must be signed in to change notification settings

Consensys/orchestrate-node

Repository files navigation

CoreStack NodeJs & Typescript SDK

CoreStack is a blockchain Transaction Orchestration system that can operate multiple chains simultaneously. It provides production grade and agnostic mechanisms for transaction crafting, nonce management, transaction signing, transaction receipt listening, transaction receipt decoding, faucet and more.

CoreStack is a microservices architecture composed of APIs & Workers. Workers communicate following publish-subscribe pattern using Apache Kafka as message broker. All messages are standardized using protobuf and GRPC.

CoreStack is Plug & Play, a user only needs to send a business protobuf message to CoreStack input topic, CoreStack then manages the full lifecycle of the transaction from crafting the transaction to decoding event logs data.

Goal

NodeJs SDK is able to

  • Send transaction via CoreStack
  • Consume mined transactions in a decoded format from CoreStack
  • Generate wallets stored in CoreStack

Quick-Start

To quickly start implement CoreStack

  1. Install
$ npm install corestack-sdk --save
  1. Import and initialize the broker
import CoreStackSDK from 'corestack-sdk'
const CoreStack = new CoreStackSDK()
// Initialize a "broker" with a kafka endpoint
const broker = CoreStack.broker('localhost:9092')

2.1 Init producer and send a CoreStack transaction request

try {
    // Create a producer that will send a transaction to CoreStack (by default, the message is sent to the topic "topic-tx-crafter", see section Broker.Producer below for further options).
    const producer = await broker.producer()
    // Send a CoreStack transaction request, see section Producer payload below for more details.
    const tx = await producer.send({
        chainId: '888',
        call: {
            contract: 'SimpleToken',
            method: 'constructor()',
        },
        gas: 2000000,
        from: '0x7e654d251da770a068413677967f6d3ea2fea9e4'
    })
    console.log('Message sent: ', tx)
    // Message sent:  { offset: 9, id: '5b479d04-8dcc-498d-af51-c3e456a070f4' }
} catch(e) {
    console.error(e)
}

2.2. Init consumer to a specific topic

try {
    // Create a consumer to listen CoreStack topics (by default, the broker is consumming "topic-tx-decoded", see section Broker.Consumer below for more details).
    const consumer = await broker.consumer()
    const consume = consumer.consume()

    consume.on('message', message => {
        console.log('Message consumed: ', message)
// Message consumed:  {
//     topic: 'topic-tx-decoded',
//     value: {
//         chain: {
//             id: '888',
//             iseip155: false
//         },
//         sender: {
//             id: '',
//             addr: '0x7e654d251da770a068413677967f6d3ea2fea9e4'
//         },
//         receiver: undefined,
//         call: {
//             contract: {
//                 registry: '',
//                 name: 'SimpleToken',
//                 tag: '',
//                 abi: '',
//                 bytecode: '',
//                 deployedbytecode: ''
//             },
//             method: {
//                 signature: 'constructor()',
//                 abi: ''
//             },
//             argsList: [],
//             quorum: undefined
//         },
//         tx: {
//             txData: {
//                 nonce: 1,
//                 to: '',
//                 value: '',
//                 gas: 2000000,
//                 gasPrice: '0x4a817c800',
//                 data: '0x60806040523480156100......7ddfc7f0d879a47389c19964d60029'
//             },
//             raw: '0xf90c65808504........714f64e08440f53c8b70'
//             ',
//             hash: '0x71c6cd4fe0d6c17dbae9dde7deb38cb14aafc2fed5eb41b0c7a3b596501da41c'
//         },
//         receipt: {
//             txHash: '0x71c6cd4fe0d6c17dbae9dde7deb38cb14aafc2fed5eb41b0c7a3b596501da41c',
//             blockHash: '0xbaca9e2e5843704a0f6bf42557bba155cc07885726f9a0faafda27e4803ed1c5',
//             blockNumber: 35,
//             txIndex: 0,
//             contractAddress: '0xe5ce65038f9d1c841a33CC816eE674F8a0E31E74',
//             postState: '',
//             status: 1,
//             bloom: 'AAIAAAAAAAAAA........AAAAAAAAAAAAAAA==',
//             logsList: [{
//                 address: '0x71B7d704598945e72E7581BAC3b070D300dc6Eb3',
//                 topicsList: [
//                     '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
//                     '0x0000000000000000000000000000000000000000000000000000000000000000',
//                     '0x0000000000000000000000007e654d251da770a068413677967f6d3ea2fea9e4'
//                 ],
//                 data: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIeGeDJurJAAAA=',
//                 event: 'Transfer(address,address,uint256)',
//                 decodedDataMap: {
//                     from: '0x0000000000000000000000000000000000000000',
//                     to: '0x7E654d251Da770A068413677967F6d3Ea2FeA9E4',
//                     value: '10000000000000000000000'
//                 },
//                 blockNumber: 128,
//                 txHash: '0xcbb8c17e59156500a668954353120b948d9397c8ba4d74935ebd32b702ca7b7a',
//                 txIndex: 0,
//                 blockHash: '0x942f9916e22c011d424a2ac9c65fbaaa529b52952d5b800fb71e1f754f2b838c',
//                 index: 0,
//                 removed: false
//             }],
//             gasUsed: 815261,
//             cumulativeGasUsed: 815261
//         },
//         errorsList: [],
//         metadata: {
//             id: '471b3dec-6f8c-4394-8fa8-ef7d3e8c59e1',
//             extraMap: {}
//         }
//     },
//     offset: 0,
//     partition: 0,
//     highWaterOffset: 1,
//     key: < Buffer 03 78 >
// }

Generate wallets stored in a vault

        //Init CoreStackSDK
        const CoreStack = new CoreStackSDK()

        // Init broker to connect to a kafka borker
        const broker = CoreStack.broker('localhost:9092')

        // Init class to generate wallets in CoreStack
        const walletGenerator = await broker.walletGenerator()

        // Generate multiple wallets asynchronously and get addresses
        const wallets = await Promise.all([
            walletGenerator.generate(),
            walletGenerator.generate(),
            walletGenerator.generate(),
            walletGenerator.generate(),
            walletGenerator.generate(),
            walletGenerator.generate(),
            walletGenerator.generate()
        ])
        console.log('Wallets generated: ' wallets)
        // Wallets generated: [
        //   '0x171cfe7D47f3F6316A5128215dc960a2812D557D',
        //   '0x9cD7eE2014a2dA810AB51166835c9cc3601D9516',
        //   '0xaAdCe2f8764c155e01711dD1bC6aE340ae4D5735',
        //   '0x28ea3Cec494F86B71b9B62482963f36f82F0b5Fd',
        //   '0xD0062dFB39180C2Ef8FCbcFEb75944e3059895ce',
        //   '0xE2d3e35c0e0731208eC09A21955E557b59aa08d6',
        //   '0x499d0E4990c8ce51cA189fb2960064442E39b98e'
        // ]

High Level Architecture

In charge of interacting with CoreStack this SDK will:

  • marshal message in protobuf format and send it to a kafka queue
  • listening a kafka queue of decoded transactions, and unmarshall messages

API

CoreStack client

const CoreStack = new CoreStackSDK()

Broker

const broker = CoreStack.broker(endpoint, [options])
  • endpoint : A string of kafka broker/host combination delimited by comma.
  • options (optional): see kafka-node options

Producer

Initilization
const producer = await broker.producer(topic, [options])
  • topic string (optional - default=topic-tx-crafter): kafka topic where to produce message.
  • options pbject (optional): see kafka-node producer options
Send a CoreStack request
const producerPayload = {
	chainId: '888',
	to: '0xc7b043d83553ffaa8a3a09e67e8d8d8333a66880',
	call: {
		method: 'transfer(address,uint256)',
		args: ['0xdbb881a51cd4023e4400cef3ef73046743f08da3', '100000']
	},
	gas: 2000000,
	from: '0x7e654d251da770a068413677967f6d3ea2fea9e4'
}
const tx = await producer.send(producerPayload)

See producer payload for more details.

Returns
  • offset : number: offset of the message sent
  • id : string: the metadata id created by CoreStack is not declared in the message
{ offset: 9, id: '5b479d04-8dcc-498d-af51-c3e456a070f4' }

Consumer

Initilization
const consumer = await broker.consumer(topics, [options])
  • topics : array[string] (optional - default=[topic-tx-decoded]) - list of topics name to consume message.
  • options : object - see kafka-node consumer options
Consume CoreStack responses
const consume = consumer.consume()

consume.on('message', message => {
    console.log('Message consumed: ', message)
})
Message consumed

The message consumes CoreStack Envelope corresponding to one transaction with consumer payload fields.

Payloads

Producer payload
Field Type Description Example
chainId string(number) the chain id to send the transaction '3', '42'
to string(hex) address of the recipient of the transaction '0x8f371DAA8A5325f53b754A7017Ac3803382bc847'
value string(number)|hex value to transfer '10000'
from string(hex) public address that will sign the transaction '0xd71400daD07d70C976D6AAFC241aF1EA183a7236'
protocol protocol object add details on the transaction protocol { name: 'quorum', tag: 'v2.2.2' }
privateFrom string(base64) when sending a private transaction, the sending party's base64-encoded public key to use. If not present and passing private_for, use the default key as configured in Quorum TransactionManager 'ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc='
privateFor array[string] when sending a private transaction, an array of the recipients' base64-encoded public keys '['ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc=']'
call call object field to detail the transaction method to call {contract: 'SimpleToken', method: 'transfer(address,uint256), args: ['0xd71400daD07d70C976D6AAFC241aF1EA183a7236', 100000]}
metadata string | metadata object name of the contract to deploy (only used for constructor(...) method) 'd4fc5002-5ba2-43e3-8591-5f46fcb4412f', {id: 'd4fc5002-5ba2-43e3-8591-5f46fcb4412f', extra: {test: 'testId'}}
Consumer payload
Field Type Description Example
topic string Name of the topic of the message consumed 'topic-tx-decoded'
value corestack response object response of the CoreStack request { chain: { id: '888' }, protocol: undefined, from: '0x7e654d251da770a068413677967f6d3ea2fea9e4', tx: { txData: [Object], raw: '0xf90c6609843b9 ... 26a70348d8af78c', hash: '0x7d2964f091440e202c67ff7853bbf2e1c73de7886698caa6b1b965348d34f055' }, receipt: { txHash: '0x7d2964f091440e202c67ff7853bbf2e1c73de7886698caa6b1b965348d34f055', blockHash: '0x9cd956c7209a0f768dcb902671273fa5f43cf4233316ee4d673acbbcb7546e92', blockNumber: 7223, txIndex: 0, contractAddress: '0x07b847b7cf6f76176cac224a1d1ddc417ef3888b', postState: '', status: 1, bloom: '0x000000000 ... 00000000000', logsList: [Array], gasUsed: 815261, cumulativeGasUsed: 815261 }, errorsList: [], args: { call: [Object], pb_private: undefined }, metadata: { id: '8c668fc5-7984-4439-816f-f396cd377bb9', extraMap: {} } }
offset number Message offset in the topic 10
partition number Partition of the message 0
key buffer partition key of the message
Call subfield (CoreStack request)
Field Type Description Example
contract string | contract object name of the contract to deploy (only used for constructor(...) method) 'SimpleToken', {name: 'SimpleToken', tag:'v0.2.0'}
method string | method object name of the contract to deploy (only used for constructor(...) method) 'transfer(address,uint256)'
{signature:'constructor()'}
args (string|number|bool|array)[] list of arguments to call the method
Possible args:
bool: true|false
uint/int: string(number|hex)|number
address: string(hex)
bytes: string(hex)
string: string
array: array[bool|uint/int|address|bytes]
['arg1', true, 200]
Contract subfield (CoreStack request)
Field Type Description Example
name string Name of the contract stored in the ABI registry 'SimpleToken'
tag string Tag version to query in the contract registry 'v0.2.0'
abi object/json ABI in json format to call '[{ constant:false, inputs:[{name:'recipient',type:'address'},{name:'amount',type:'uint256'}], name:'transfer', outputs:[ {name:'',type:'bool'} ] payable: false, stateMutability: 'nonpayable', type: 'function' }]'
bytecode string(hex) Contract bytecode '0x608060405234 .... e578063995fac'
CoreStack response subfield
Field Type Description Example
chain object ChainId of the CoreStack request { id: '888' }
protocol protocol object Protocol to be used in the CoreStack request { name: 'quorum', tag: 'v2.2.2', extraMap: {} }
from string Address supposed to sign the CoreStack request '0x7e654d251da770a068413677967f6d3ea2fea9e4'
tx tx object Transaction detail to be executed { txData: [Object], raw: '0xf90c6609843b9 ... 26a70348d8af78c', hash: '0x7d2964f091440e202c67ff7853bbf2e1c73de7886698caa6b1b965348d34f055' }
receipt receipt object Receipt of the transaction { txHash: '0xf37291705cd42b6d2a49579a4b6f639eded6964f1c2c41fad920170c22c49534', blockHash: '0x2311426739570437d12b0d266ac6efc06e231f4bbda2f7b635e3b4918ff16be6', blockNumber: 12220907, txIndex: 0, contractAddress: '0x0000000000000000000000000000000000000000', postState: '', status: 1, bloom: '0x00000 ... 000000', logsList: [], gasUsed: 114252, cumulativeGasUsed: 114252 }
errorsList array list of errors from CoreStack when processing this CoreStack request [ { message: 'rpc error: code = Internal desc = Could not load by TxHash pg: no rows in result set chain_id:"42" tx_hash:"0xf37291705cd42b6d2a49579a4b6f639eded6964f1c2c41fad920170c22c49534" ', code: 0, extraMap: [] } ]
args args object arguments of the transaction (including private args if needed) { call: {contract: [Object], method: [Object], args: []}, pb_private: undefined }
metadata metadata object Metadata included in the CoreStack request { id: '8c668fc5-7984-4439-816f-f396cd377bb9', extraMap: {} }
Receipt subfield (CoreStack response)
Field Type Description Example
txHash string hash this transaction '0xa6e9939bf85a119e38bba74a43b86fb2a6a00482c064954c6c4d6991399579c1'
blockHash string hash of the block where this transaction was in '0x7bf0bd196dc53693c7d74c862e22179477c163d8f56da37313f5b3aae3aa8f59'
blockNumber number block number where this transaction was in 683
txIndex number index of this transaction inside the block 0
contractAddress string(hex) the contract address created, if the transaction was a contract creation, otherwise null '0xe5ce65038f9d1c841a33cc816ee674f8a0e31e74'
bloom string(hex) the bloom filter for the logs of the block '0x000200000 ... 0000000000'
status number 0 indicates transaction failure , 1 indicates transaction succeeded 1 or 0
postState string NA NA
logsList array[object] list of logs (aka. events) of the transaction [ { address: '0xe5ce65038f9d1c841a33cc816ee674f8a0e31e74', topicsList: [Array], data: '0x000000000 ... e0c9bab2400000', event: 'Approval(address,address,uint256)', decodedDataMap: [Object], blockNumber: 483, txHash: '0xa6e9939bf85a1 ... 6c4d6991399579c1', txIndex: 0, blockHash: '0x30d88cdd7511 ... 3f09cde23d681a6', index: 0, removed: false } ]
gasUsed number the amount of gas used by this specific transaction alone 815261
cumulativeGasUsed number the total amount of gas used when this transaction was executed in the block 815261
Log subfield (CoreStack response)
Field Type Description Example
address string contract address '0xa6e9939bf85a119e38bba74a43b86fb2a6a00482c064954c6c4d6991399579c1'
topicsList array[string(hex)] list of the event signature and hashes of event's indexed arguments '[ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000007e654d251da770a068413677967f6d3ea2fea9e4' ]'
data string data of the non-indexed arguments of the event '0x00000000000000000000000000000000000000000000021e19e0c9bab2400000'
event string event signature 'Approval(address,address,uint256)'
decodedDataMap object decoded event with value as string format. Arrays should be parsed as JSON { owner: '0x0000000000000000000000000000000000000000', spender: '0x7E654d251Da770A068413677967F6d3Ea2FeA9E4', value: '10000000000000000000000' }
blockNumber number block number that includes this event 50
txHash number transaction hash that includes this event '0xa6e9939bf85a119e38bba74a43b86fb2a6a00482c064954c6c4d6991399579c1'
txIndex number transaction index that includes this event 0
blockHash string(hex) block hash that includes this event '0xc448362abbebe286efdd6c64b4fbe2e289748159b9edfdb973f592fc4e6787ec'
index number index of the event 0
removed bool true when the log was removed, due to a chain reorganization. false if its a valid log. true or false
Args subfield (CoreStack response)
Field Type Description Example
call call object fields related to the transaction call (contract, method, arguments) { contract: { id: { registry: '', name: 'SimpleToken', tag: '' }, abi: '', bytecode: '', methodsList: [], eventsList: [], deployedbytecode: '' }, method: { signature: 'constructor()', abi: '' }, argsList: [] }
pb_private private object fields related to privacy protocol
Call subfield (CoreStack response)
Field Type Description Example
contract object fields related to the contract for the CoreStack Request { id: { registry: '', name: 'SimpleToken', tag: '' }, abi: '', bytecode: '', methodsList: [], eventsList: [], deployedbytecode: '' }
method method object fields related to the method called for the CoreStack Request { signature: 'transfer(address,uint256)', abi: '' }
argsList array inputs of method call [ '0xdbb881a51cd4023e4400cef3ef73046743f08da3', '0x186a0' ]
Private subfield (CoreStack response)
Field Type Description Example
privateFrom string When sending a private transaction, the sending party's base64-encoded public key to use 'ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc='
privateFor array[string] When sending a private transaction, an array of the recipients' base64-encoded public keys ['ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc=']
privateTxType string For Quorum >=2.2.1 support NA
Tx subfield (CoreStack response)
Field Type Description Example
txData txData object fields used to sign the transaction { nonce: 4, to: '0xc7b043d83553ffaa8a3a09e67e8d8d8333a66880', value: undefined, gas: 2000000, gasPrice: '1000000000', data: '0xa9059cbb00000000 ... 0000000186a0' }
raw string signed transaction '0xf8ab04843b9aca ... 6e7f832b3c96d49f8e2'
hash array transaction hash stored in the CoreStack request '0x0a0cafa26ca3f411e6629e9e02c53f23713b0033d7a72e534136104b5447a210'
TxData subfield (CoreStack response)
Field Type Description Example
nonce number nonce used for the transaction 4
to string(hex) account or smart contract address '0xc7b043d83553ffaa8a3a09e67e8d8d8333a66880'
value string(number) value to transfer '20000'
gas number gas limit to be consumed for this transaction 2000000
gasPrice string(number) gas price used or this transaction '2000000'
data string(hex) Hash of the method signature (4 bytes) followed by encoded parameters '0xa9059cbb00000000000 ... 0002386f26fc10000'
Protocol subfield (Common)
Field Type Description Example
name string name of the protocol `pantheon`, `quorum`
tag string version tag of the protocol 'v2.2.4'
extra object extra information {test: 'test'}
Method subfield (Common)
Field Type Description Example
signature string Signature of the method referenced in the ABI registry 'transfer(address,uint256)'
'constructor()'
abi object/json Method ABI in json format to craft and call '{ constant:false, inputs:[{name:'recipient',type:'address'},{name:'amount',type:'uint256'}], name:'transfer', outputs:[ {name:'',type:'bool'} ] payable: false, stateMutability: 'nonpayable', type: 'function' }'
Metadata subfield (Common)
Field Type Description Example
id string ID to attach in the CoreStack request 'a78b0e65-2ae5-48c3-97d0-0c3a2bbe8110'
extra object extra metadata to attach in the CoreStack request {extra1: 'testExtra1', extra2: 'testExtra2'}

Technical Architecture

alt CoreStack-architecture