deVote Explorer interfaces with a blockchain node to first extract all the levelDB data. It then stores it in a SQLite database to present the data in a searchable format.
For more details, refer to Documentation - Section 6.9.7 Backend Operations - Page 63:66.
Make sure you have Node.js installed.
- Install dependencies.
npm install
- Set your local variables in .env file. Make sure to set BLKS_PATH properly, it's where blocks are saved.
PORT = 9000
SQLITEDB_NAME = "deVote"
BLKS_PATH = "M:\\GradutaionProject\\DeVote\\Blocks"
PROTO_FILE_NAME = "block"
MINS_TO_SYNC_DBS = 0.5
- Start the server, it will be running on your local IP address.
npm start
Query JSON data for blocks, transactions, candidates, status of running election and location of voting machines.
GET /blocks/block-height/50
{
"result": {
"Height": 50,
"PrevHash": "2B3CA86D514B5A27D4C1D2365E04A1A7D59E14CFCDF8935677738E301BF692F2",
"Timestamp": 1649069215442,
"MerkleRoot": "514E4FC4D7BF898C4694EF88F99EE88069778D307032097AD4112ED62849F1DD",
"Hash": "81D10DABE0F6C51D097DAFA7675473DFC1370621D13C46274706CC457629CFAE",
"Miner": "Test33",
"nTx": 4
}
}
GET /blocks/block-hash/81D10DABE0F6C51D097DAFA7675473DFC1370621D13C46274706CC457629CFAE
{
"result": {
"Height": 50,
"PrevHash": "2B3CA86D514B5A27D4C1D2365E04A1A7D59E14CFCDF8935677738E301BF692F2",
"Timestamp": 1649069215442,
"MerkleRoot": "514E4FC4D7BF898C4694EF88F99EE88069778D307032097AD4112ED62849F1DD",
"Hash": "81D10DABE0F6C51D097DAFA7675473DFC1370621D13C46274706CC457629CFAE",
"Miner": "Test33",
"nTx": 4
}
}
GET /transactions/tx-hash/66FEF9BBE3B5924923114CC84D46DFE333C3E8F928FF6472108B2FCAC036EE37
{
"result": {
"Date": 1650144307640,
"Hash": "66FEF9BBE3B5924923114CC84D46DFE333C3E8F928FF6472108B2FCAC036EE37",
"Elector": "elector3148",
"Elected": "elected3148",
"BlockHeight": 150
}
}
GET /transactions/tx-block-height/150
{
"result": [
{
"Date": 1650144307611,
"Hash": "672C625CE5F4499088C2D5305BD3752255B882434F73D4FAD1C2D2806E243F34",
"Elector": "elector0148",
"Elected": "elected0148",
"BlockHeight": 150
},
{
"Date": 1650144307622,
"Hash": "32A493456E2EAF3530A4954D42F97A4E0AFE88814B8F6484B039BBCCC79EE4C7",
"Elector": "elector1148",
"Elected": "elected1148",
"BlockHeight": 150
},
{
"Date": 1650144307629,
"Hash": "DBAFE2FAA90FC4073D8E8AA3C38DA754D9B4A06B06A687DC4F37AF7CDECE7C99",
"Elector": "elector2148",
"Elected": "elected2148",
"BlockHeight": 150
},
{
"Date": 1650144307640,
"Hash": "66FEF9BBE3B5924923114CC84D46DFE333C3E8F928FF6472108B2FCAC036EE37",
"Elector": "elector3148",
"Elected": "elected3148",
"BlockHeight": 150
}
]
}
Rather than retrieving a complete set of blocks/transactions using a single request which its response will be slow, we are using bi-directional cursor based pagination technique that allows the client to ask for one subset at a time and the API responds with the corresponding subset, along with a pagination object for how the clinet can retrieve the next/previous subset of blocks/transactions.
The complete endpoint has the following parameters.
GET /blocks?limit=20&heightCursor=prev_131
GET /transactions?limit=20&idCursor=prev_2
-
limit
- The maximum number of blocks to fetch. The default limit value is 20. The maximum limit value is 50. -
heightCursor/idCursor
- The cursor serving as- a pointer to a specific block/transaction, where last request left off.
- an indicator for whether we want set of blocks/transaction next or previous to the pointer.
We are using the unique, sequential columns : blockHeight and transactionTimestamp.
On the initial request, you don't pass a heightCursor/idCursor parameter. You just request the endpoint without parameters.
GET /blocks
GET /transactions
Or you can set a limit parameter to specify how many blocks/transactions to retrieve per request.
GET /blocks/?limit=50
GET /transactions/?limit=50
The server's response to the initial request will include a pagination object that includes a prev property when there are additional blocks/transactions to be retrieved.
You use prev's link to request more previous blocks/transactions.
"pagination": {
"prev": "https://devote-explorer-backend.herokuapp.com/blocks?limit=20&heightCursor=prev_131",
"more": true,
"max": 150
}
"pagination": {
"prev": "https://devote-explorer-backend.herokuapp.com/transactions?limit=20&idCursor=prev_2",
"more": true,
"max": 2
}
Show full response for /blocks
{
"pagination": {
"prev": "https://devote-explorer-backend.herokuapp.com/blocks?limit=20&heightCursor=prev_131",
"more": true,
"max": 150
},
"result": [
{
"Height": 150,
"PrevHash": "6ED794D084F6B99B5088798F0E62FA5AA537D673665B79FFC018B08A95446535",
"Timestamp": 1650144307652,
"MerkleRoot": "6BEFEC9C11B0B02C27BC1E68A1CDD2C7919832FC8AC3B887E140D3AE51856534",
"Hash": "43034489A9AEDE4150562E244C3D4CC526B8E7597E444A5D78965ECF653EB020",
"Miner": "Test33",
"nTx": 4
},...
{
"Height": 131,
"PrevHash": "9321074BEE883F85B7977A750857E569E4E497E336B7029CDBFE37754FD32101",
"Timestamp": 1650144306527,
"MerkleRoot": "9D66249A7A1D2778744C8DDF695C416F2C40FD294EFCF12C12544E363C436ED9",
"Hash": "F2AAA55AD5A6C9C002DB70E0BBCC4C3602E5A86E481697B8B22F60BDB0AFA334",
"Miner": "Test33",
"nTx": 4
}
]
}
Show full response for /transactions
{
"pagination": {
"prev": "https://devote-explorer-backend.herokuapp.com/transactions?limit=20&idCursor=prev_2",
"more": true,
"max": 2
},
"result": [
{
"Date": 1650144307640,
"Hash": "66FEF9BBE3B5924923114CC84D46DFE333C3E8F928FF6472108B2FCAC036EE37",
"Elector": "elector3148",
"Elected": "elected3148",
"BlockHeight": 150
},...
{
"Date": 1650144307329,
"Hash": "00DE2585B9FA5208A020F942B548327AC03C4B2A8B00F5C50F39D712BF0DADF7",
"Elector": "elector0144",
"Elected": "elected0144",
"BlockHeight": 146
}
]
}
On subsequent requests, there should be a heightCursor/idCursor parameter
GET /blocks?limit=20&heightCursor=prev_131
GET /transactions?limit=20&idCursor=prev_2
The server's response to the subsequent request will include a pagination object that includes prev and next properties when there are additional blocks/transactions to be retrieved.
Response :
"pagination": {
"prev": "https://devote-explorer-backend.herokuapp.com/blocks?limit=20&heightCursor=prev_111",
"next": "https://devote-explorer-backend.herokuapp.com/blocks?limit=20&heightCursor=next_130",
"more": true,
"max": 150
}
"pagination": {
"prev": "https://devote-explorer-backend.herokuapp.com/transactions?limit=20&idCursor=prev_1650144307018",
"next": "https://devote-explorer-backend.herokuapp.com/transactions?limit=20&idCursor=next_2",
"more": true,
"max": 1650144307640
}
Note that the pagination's object includes a next property, which points to block height of the next page.
On your next request, you can either fetch prev or next set of blocks/transactions
For example, to retrieve the next older set of the blocks [Block 110 : Block 91] .
- Request the link of the prev property you received on the last request.
GET /blocks?limit=20&heightCursor=prev_111
to retrieve the next newer set of the blocks [Block 150 : Block 131].
- Request the link of the next property you received on the last request.
GET /blocks?limit=20&heightCursor=next_130
As you make more subsequent requests to retrieve previous blocks/transactions, you will eventually receive a response with more
as false
, indicating the end of the entire set.
{
"pagination": {
"next": "https://devote-explorer-backend.herokuapp.com/blocks?limit=20&heightCursor=next_10",
"more": false,
"max": 150
},
"result": [
{
"Height": 10,
"PrevHash": "25C651CA1EA3C903E01D7F945F833202495690358EB1882EA15FC491EA322AB1",
"Timestamp": 1650144300173,
"MerkleRoot": "5A3EFD3316319EB0953EE5DAA4B7848CCAC6AFF35EF2B4642FD7BC9837E6D77E",
"Hash": "9F9982478BF2AF82085061D58ECFB7EA4DD12B40CB9B1BC5E524BBE9D1A5F0E7",
"Miner": "Test33",
"nTx": 4
},...
{
"Height": 1,
"PrevHash": null,
"Timestamp": 1650144298861,
"MerkleRoot": null,
"Hash": "B60D6D9A62B48D4B48D3F6500B6552F3A6C2B772949E94C5C3BAB0F6CF9BE77B",
"Miner": "deVote",
"nTx": 1
}
]
}
If there are any errors, the server's response will includes an errors object having a list of errors.
/blocks/block-height/200
{
"errors": [
{
"code": "404",
"status": "Not Found",
"detail": "No block was found for height : 200"
}
]
}
/blocks/block-hash/RT0D6D9A62B48D4B48D3F6500B6552F3A6C2B772949E94C5C3BAB0F6CF9BE77B
{
"errors": [
{
"code": "404",
"status": "Not Found",
"detail": "No block was found for hash : RT0D6D9A62B48D4B48D3F6500B6552F3A6C2B772949E94C5C3BAB0F6CF9BE77B"
}
]
}
/transactions/tx-block-height/200
{
"errors": [
{
"code": "404",
"status": "Not Found",
"detail": "No transaction was found for blockHeight : 200"
}
]
}
/transactions/tx-hash/RT0D6D9A62B48D4B48D3F6500B6552F3A6C2B772949E94C5C3BAB0F6CF9BE77B
{
"errors": [
{
"code": "404",
"status": "Not Found",
"detail": "No transaction was found for hash : RT0D6D9A62B48D4B48D3F6500B6552F3A6C2B772949E94C5C3BAB0F6CF9BE77B"
}
]
}
The required properties of json body are
- id
- name
- lat
- lng
POST /vms
request body : (application/json)
{
"id": "0F0F1C30FBB75B87EB11A4934A9520FEF31D43609E3A1261FF63A31D73347F4F1828",
"name": "Machine1",
"lat": "39.354503136666662",
"lng": "33.313432350000007"
}
{
"result": {
"id": "0F0F1C30FBB75B87EB11A4934A9520FEF31D43609E3A1261FF63A31D73347F4F1828",
"lat": "39.354503136666662",
"lng": "33.313432350000007",
"name": "Machine1"
}
}
GET /vms
{
"result": [
{
"id": "0F0F1C30FBB75B87EB11A4934A9520FEF31D43609E3A1261FF63A31D73347F4F1828",
"lat": "39.354503136666662",
"lng": "33.313432350000007",
"name": "Machine1"
}
]
}
POST /vms
request body : (application/json)
{
"id": "500",
"name": "VM1",
"lat": "40.286548",
"lng": "44.739853"
}
{
"request": {
"id": "500",
"name": "VM1",
"lat": "40.286548",
"lng": "44.739853"
},
"errors": [
{
"code": "400",
"status": "Bad Request",
"detail": "Your request parameters values are invalid"
}
]
}