Broadcast a transaction
This guide explains how to create a transaction object of a specific transaction type, and how to broadcast it to the network.
To achieve this a small script in Javascript needs to be written, that utilizes Lisk Elements packages to create the transaction object, sign it, and send it to the network.
For the default transaction types, it is also possible to use Lisk Commander to create and broadcast transactions from the command-line or Lisk Desktop to send a transaction via a user-friendly UI. |
Ensure that Node.js is the latest LTS (version 12 or higher), and create a Node.js project by creating a package.json
file inside of your current folder.
mkdir create-tx
cd create-tx
node --version
# v12.15.0
npm init --yes
npm i @liskhq/lisk-transactions
npm i @liskhq/lisk-cryptography
npm i @liskhq/lisk-api-client
Now create a new file index.js
that will contain the script to generate a transaction, sign it and then post it to the network.
The full script can be viewed at 3. Broadcast a transaction.
1. Create a transaction object
const transactions = require('@liskhq/lisk-transactions');
const {getNetworkIdentifier} = require('@liskhq/lisk-cryptography');
const networkIdentifier = getNetworkIdentifier(
"23ce0366ef0a14a91e5fd4b1591fc880ffbef9d988ff8bebf8f3666b0c09597d",
"Lisk",
);
const tx = new transactions.TransferTransaction({
asset: {
amount: '1',
recipientId: '1L',
},
networkIdentifier: networkIdentifier,
});
console.log(tx.stringify());
{
"type":8,
"timestamp":0,
"senderPublicKey":"",
"senderId":"",
"fee":"10000000",
"signatures":[
],
"asset":{
"amount":"1",
"recipientId":"1L"
}
}
The HelloTransaction
from the Hello World app is used as an example here.
const HelloTransaction = require('../hello_transaction');
const {getNetworkIdentifier} = require('@liskhq/lisk-cryptography');
const networkIdentifier = getNetworkIdentifier(
"23ce0366ef0a14a91e5fd4b1591fc880ffbef9d988ff8bebf8f3666b0c09597d",
"Lisk",
);
const tx = new HelloTransaction({
asset: {
hello: 'world',
},
networkIdentifier: networkIdentifier,
});
console.log(tx.stringify());
{
"type": 20,
"timestamp": 0,
"senderPublicKey": "",
"senderId": "",
"fee": "0",
"signatures": [],
"asset": {
"hello": "world"
}
}
2. Sign a transaction
Before any transaction can be sent to the network, it is required for the sender’s account to sign the transaction object.
See the Lisk protocol for more information about the signature scheme. |
const transactions = require('@liskhq/lisk-transactions');
const {getNetworkIdentifier} = require('@liskhq/lisk-cryptography');
const networkIdentifier = getNetworkIdentifier(
"23ce0366ef0a14a91e5fd4b1591fc880ffbef9d988ff8bebf8f3666b0c09597d",
"Lisk",
);
const tx = new transactions.TransferTransaction({
asset: {
amount: '1',
recipientId: '1L',
},
networkIdentifier: networkIdentifier,
});
tx.sign('creek own stem final gate scrub live shallow stage host concert they');
console.log(tx.stringify());
{
"id":"14444765956109766257",
"type":8,
"timestamp":0,
"senderPublicKey":"5c554d43301786aec29a09b13b485176e81d1532347a351aeafe018c199fd7ca",
"senderId":"11237980039345381032L",
"fee":"10000000",
"signature":"49d5824b9008b2005a554d984dedf8986b8bb54328dc5bf4c6a61fcdca6115a5ac0e17b5ec4c24bdaaae4f3be2cf808f514d2b74c506c6df9fcfcfad1caaa702",
"signatures":[],
"asset":{
"amount":"1",
"recipientId":"1L"
}
}
The HelloTransaction
from the Hello World app is used as an example here.
const HelloTransaction = require('../hello_transaction');
const {getNetworkIdentifier} = require('@liskhq/lisk-cryptography');
const networkIdentifier = getNetworkIdentifier(
"23ce0366ef0a14a91e5fd4b1591fc880ffbef9d988ff8bebf8f3666b0c09597d",
"Lisk",
);
const tx = new HelloTransaction({
asset: {
hello: 'world',
},
networkIdentifier: networkIdentifier,
});
tx.sign('wagon stock borrow episode laundry kitten salute link globe zero feed marble');
console.log(tx.stringify());
{
"id": "11559016465370069697",
"type": 20,
"timestamp": 0,
"senderPublicKey": "c094ebee7ec0c50ebee32918655e089f6e1a604b83bcaa760293c61e0f18ab6f",
"senderId": "16313739661670634666L",
"fee": "0",
"signature": "7524e854fe7da042606e4893e61e2515ec1956f70231422973fa9369d345eded998e5a9ba90902e51cb0ac8fdce88fca645fb44e7085fe7ed7f1b29499ae570c",
"signatures": [],
"asset": {
"hello": "world"
}
}
3. Broadcast a transaction
const transactions = require('@liskhq/lisk-transactions');
const {getNetworkIdentifier} = require('@liskhq/lisk-cryptography');
const { APIClient } = require('@liskhq/lisk-api-client');
// Constants
const API_BASEURL = 'http://localhost:4000'; (1)
const networkIdentifier = getNetworkIdentifier(
"23ce0366ef0a14a91e5fd4b1591fc880ffbef9d988ff8bebf8f3666b0c09597d",
"Lisk",
);
// Initialize
const api = new APIClient([API_BASEURL]);
const tx = new transactions.TransferTransaction({
asset: {
amount: '1',
recipientId: '1L',
},
networkIdentifier: networkIdentifier,
});
tx.sign('creek own stem final gate scrub live shallow stage host concert they');
api.transactions.broadcast(tx.toJSON()).then(res => {
console.log("++++++++++++++++ API Response +++++++++++++++++");
console.log(res.data);
console.log("++++++++++++++++ Transaction Payload +++++++++++++++++");
console.log(tx.stringify());
console.log("++++++++++++++++ End Script +++++++++++++++++");
}).catch(err => {
console.log(JSON.stringify(err.errors, null, 2));
});
1 | http://localhost:4000 will post the transaction to a node that runs locally (this is indicated by the url http://localhost ), and is connected to the Devnet, (this is indicated by the port number 4000 ).
Replace http://localhost:4000 with the url of the node you want to broadcast the transaction to. |
++++++++++++++++ API Response +++++++++++++++++
{ message: 'Transaction(s) accepted' }
++++++++++++++++ Transaction Payload +++++++++++++++++
{
"id": "14444765956109766257",
"type": 8,
"timestamp": 0,
"senderPublicKey": "5c554d43301786aec29a09b13b485176e81d1532347a351aeafe018c199fd7ca",
"senderId": "11237980039345381032L",
"fee": "10000000",
"signature": "49d5824b9008b2005a554d984dedf8986b8bb54328dc5bf4c6a61fcdca6115a5ac0e17b5ec4c24bdaaae4f3be2cf808f514d2b74c506c6df9fcfcfad1caaa702",
"signatures": [],
"asset": {
"amount": "1",
"recipientId": "1L"
}
}
++++++++++++++++ End Script +++++++++++++++++
The HelloTransaction
from the Hello World app is used as an example here.
const HelloTransaction = require('./hello');
const {getNetworkIdentifier} = require('@liskhq/lisk-cryptography');
const { APIClient } = require('@liskhq/lisk-api-client');
// Constants
const API_BASEURL = 'http://localhost:4000';
const networkIdentifier = getNetworkIdentifier(
"23ce0366ef0a14a91e5fd4b1591fc880ffbef9d988ff8bebf8f3666b0c09597d",
"Lisk",
);
// Initialize
const api = new APIClient([API_BASEURL]);
const tx = new HelloTransaction({
asset: {
hello: 'world',
},
networkIdentifier: networkIdentifier,
});
tx.sign('wagon stock borrow episode laundry kitten salute link globe zero feed marble');
api.transactions.broadcast(tx.toJSON()).then(res => {
console.log("++++++++++++++++ API Response +++++++++++++++++");
console.log(res.data);
console.log("++++++++++++++++ Transaction Payload +++++++++++++++++");
console.log(tx.stringify());
console.log("++++++++++++++++ End Script +++++++++++++++++");
}).catch(err => {
console.log(JSON.stringify(err.errors, null, 2));
});
++++++++++++++++ API Response +++++++++++++++++
{ message: 'Transaction(s) accepted' }
++++++++++++++++ Transaction Payload +++++++++++++++++
{
"id": "2039423469691006779",
"type": 20,
"timestamp": 0,
"senderPublicKey": "5c554d43301786aec29a09b13b485176e81d1532347a351aeafe018c199fd7ca",
"senderId": "11237980039345381032L",
"fee": "0",
"signature": "532c3297451bc7f14fe7b80b38d55b4cc9527b1d13a6f353fa7f13b8af973e69d47f87d4108e5768e0a9e0e6a426de6ae0751005dd126f04fa34f97882bfc509",
"signatures": [],
"asset": {
"hello": "world"
}
}
++++++++++++++++ End Script +++++++++++++++++
4. What happens to a transaction after a node receives it?
The transaction will be validated by the node, and added to the transaction pool if it is valid.
To validate the transaction, it will execute the logic defined in the validateAsset()
method.
The node will also inform its peer nodes about the new transaction, so in turn all of them will validate the transaction and add it to their transaction pool as well. If the transaction is added to the transaction pool of a forging node, the transaction will be included in one of the next new blocks, if it is not included already by another forger.
Once the transaction is included into a block, it becomes part of the blockchain.
By including a transaction into a block, the node executes the logic defined in the applyAsset()
method of the transaction.
To ensure that the transaction is final, it is recommended to wait for at least 150 blocks.
It is possible to verify the finality of a particular transaction via the API:
-
GET
api/node/status
to receive the node status data. It should contain a propertychainMaxHeightFinalized
which describes the highest block height of the network, that is already finalized. -
GET
api/transactions?id=<TRANSACTION_ID>
to receive the data of the transaction that you wish to check for finality. Replace<TRANSACTION_ID>
with the ID of the transaction. The data should contain a propertyheight
, which indicates the block height at the time the transaction was included into the blockchain. -
The final step here is now to compare the two values. Hence the transaction is final, if
chainMaxHeightFinalized > height
.
If a transaction is finalized, it becomes a permanent part of the blockchain and cannot be removed anymore. |