- CoreStack NodeJs & Typescript SDK
- Goal
- Quick-Start
- Generate wallets stored in a vault
- High Level Architecture
- API
- CoreStack client
- Broker
- Producer
- Consumer
- Payloads
- Producer payload
- Consumer payload
- Call subfield (CoreStack request)
- Contract subfield (CoreStack request)
- CoreStack response subfield
- Receipt subfield (CoreStack response)
- Log subfield (CoreStack response)
- Args subfield (CoreStack response)
- Call subfield (CoreStack response)
- Private subfield (CoreStack response)
- Tx subfield (CoreStack response)
- TxData subfield (CoreStack response)
- Protocol subfield (Common)
- Method subfield (Common)
- Metadata subfield (Common)
- Technical Architecture
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.
NodeJs SDK is able to
- Send transaction via CoreStack
- Consume mined transactions in a decoded format from CoreStack
- Generate wallets stored in CoreStack
To quickly start implement CoreStack
- Install
$ npm install corestack-sdk --save
- 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 >
// }
//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'
// ]
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
const CoreStack = new CoreStackSDK()
const broker = CoreStack.broker(endpoint, [options])
endpoint
: A string of kafka broker/host combination delimited by comma.options
(optional): see kafka-node options
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
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.
offset
: number: offset of the message sentid
: string: the metadata id created by CoreStack is not declared in the message
{ offset: 9, id: '5b479d04-8dcc-498d-af51-c3e456a070f4' }
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
const consume = consumer.consume()
consume.on('message', message => {
console.log('Message consumed: ', message)
})
The message consumes CoreStack Envelope
corresponding to one transaction with consumer payload fields.
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'}} |
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 |
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] |
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' |
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: {} } |
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 |
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 |
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 |
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' ] |
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 |
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' |
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' |
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'} |
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' }' |
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'} |