-
Notifications
You must be signed in to change notification settings - Fork 0
API test - Verify transaction from Microblock to anchorblock #2
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| { | ||
| "name": "stacks", | ||
| "version": "1.0.0", | ||
| "description": "", | ||
| "main": "index.js", | ||
| "devDependencies": { | ||
| "@types/node": "^18.11.12", | ||
| "prettier": "2.8.1", | ||
| "ts-node": "^10.9.1", | ||
| "typescript": "^4.9.4" | ||
| }, | ||
| "scripts": { | ||
| "test": "ts-node ./script/index.ts" | ||
| }, | ||
| "keywords": [], | ||
| "author": "", | ||
| "license": "ISC", | ||
| "dependencies": { | ||
| "axios": "^1.2.1" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # apiTest | ||
| apiTest is a node script for testing transactions on micro block api and anchor block api. | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - npm installed and Node v16.* | ||
| - Stacks CLI on your machine [@stacks/cli](https://www.npmjs.com/package/@stacks/cli) | ||
|
|
||
| ### Run script | ||
| 1. Go to the root of the project and do `npm install`. Make sure you have satisfied the above Prerequisites. | ||
| 2. Run the script: | ||
| ```sh | ||
| $ npm run test | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { triggerAnchorBlockTests } from "./tests/anchor.blocks.spec"; | ||
|
|
||
| const anchorBlockOptions = { | ||
| accountId: "ST3C4YVASFG37P8E6210J9KHZJB4MF8ZRBQFKH41N", | ||
| recipient: "ST297VG59W96DPGBT13SGD542QE1XS954X4ZSWW2J", | ||
| }; | ||
|
|
||
| // trigger anchor block testing script | ||
| triggerAnchorBlockTests(anchorBlockOptions); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import * as util from "util"; | ||
| import * as child_process from "child_process"; | ||
| const exec = util.promisify(child_process.exec); | ||
| let interval, | ||
| triggerCount = 0, | ||
| foundMicroBlock = false; | ||
|
|
||
| import { getAccountDetail, getMicroBlock, getAnchorBlock } from "../utils/curl"; | ||
| import { wait } from "../utils/helper"; | ||
|
|
||
| const runAnchorBlockTest = async (options: any) => { | ||
| triggerCount++; | ||
| console.log(`Trigger script count: ${triggerCount}`); | ||
|
|
||
| const { accountId, recipient } = options; | ||
| try { | ||
| // step 1: Fetch nonce from account id | ||
| console.log("Fetching Nonce..."); | ||
| const nonce = await fetchNonce(accountId); | ||
| console.log("Nonce fetched", nonce); | ||
|
|
||
| // step 2: Run the stx command for send_tokens to fetch the TXID | ||
| console.log("Sending tokens and fetching TXID..."); | ||
| const txid = await sendTokens(nonce, recipient); | ||
| console.log("TXID fetched", txid); | ||
|
|
||
| // step 3: CURL the microblock API to make sure that TXID exist | ||
| if (!foundMicroBlock) { | ||
| await wait(10000); | ||
| console.log("Calling microblock API..."); | ||
| const isTXIDExistMB = await checkTXIDInMicroBlock(txid); | ||
| if (isTXIDExistMB) { | ||
| foundMicroBlock = true; | ||
| console.log(`The transaction ${txid} is present in Micro Block API`); | ||
| } else { | ||
| console.log( | ||
| `The transaction ${txid} is not present in Micro Block API` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // step 4: CURL the Anchor block API to make sure that TXID exist | ||
| console.log("Calling Anchor block API..."); | ||
| const isTXIDExistAB = await checkTXIDInAnchorBlock(txid); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think another valuable API request would be to check if https://stacks-node-api.mainnet.stacks.co/extended/v1/tx/{tx_id} confirms the transaction exists in the same anchor block and with the correct properties
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can add that as well |
||
| if (isTXIDExistAB) { | ||
| console.log(`The transaction ${txid} is present in Anchor block API`); | ||
| process.exit(0); | ||
| } else { | ||
| console.log(`The transaction ${txid} is not present in Anchor block API`); | ||
| } | ||
| if (triggerCount === 5) { | ||
| console.log("Time limit reached: 30 minutes"); | ||
| clearInterval(interval); | ||
| process.exit(0); | ||
| } | ||
| } catch (e) { | ||
| console.error( | ||
| "An error occurred while processing the anchor block script", | ||
| e | ||
| ); | ||
| clearInterval(interval); | ||
| process.exit(1); | ||
| } | ||
| }; | ||
|
|
||
| export function triggerAnchorBlockTests(options: any) { | ||
| runAnchorBlockTest(options); | ||
|
|
||
| interval = setInterval(() => { | ||
| runAnchorBlockTest(options); | ||
| }, 6 * 60 * 1000); // every 6 minutes | ||
| } | ||
|
|
||
| async function fetchNonce(accountId: string): Promise<number> { | ||
| const accountDetail = await getAccountDetail(accountId); | ||
| if (!accountDetail) { | ||
| console.log(`No account detail found for account id - ${accountId}`); | ||
| return 0; | ||
| } | ||
| return accountDetail.nonce; | ||
| } | ||
|
|
||
| async function sendTokens(nonce: number, recipient: string): Promise<any> { | ||
| const { stdout, stderr } = await exec( | ||
| `stx -t send_tokens ${recipient} 12345 999 ${nonce} fd3609e8b9352facf360c950d362afe8f0f108b9041750f54b017efc479efeb001` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not too familiar with the stx cli... is any of these inputs sensitive info? In that case you could save it as a Github actions secret in the repo and use it as an input via an ENV var
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure I can do that this is a test account so I just threw the private key into the script but I do see your point. |
||
| ); | ||
| if (stderr) { | ||
| console.error( | ||
| `An error occurred while sending the tokens to recipient ${recipient}` | ||
| ); | ||
| return; | ||
| } | ||
| const filteredResponse = stdout.replace(/(\r\n|\n|\r|\s)/gm, ""); | ||
| return filteredResponse.split(",")[0].split(":")[1].replace(/\'/g, ""); | ||
| } | ||
|
|
||
| async function checkTXIDInMicroBlock(txid: string): Promise<boolean> { | ||
| const microBlockData = await getMicroBlock(); | ||
| if (!microBlockData) { | ||
| console.log(`No micro block data found`); | ||
| return false; | ||
| } | ||
| const isTXIDExist = microBlockData.results.some( | ||
| (result) => result.tx_id === txid | ||
| ); | ||
| return isTXIDExist; | ||
| } | ||
|
|
||
| async function checkTXIDInAnchorBlock(txid: string): Promise<boolean> { | ||
| const anchorBlockData = await getAnchorBlock(); | ||
| if (!anchorBlockData) { | ||
| console.log(`No anchor block data found`); | ||
| return false; | ||
| } | ||
| const isTXIDExist = anchorBlockData.results.some( | ||
| (result) => result.tx_id === txid | ||
| ); | ||
| return isTXIDExist; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import axios from "axios"; | ||
|
|
||
| export async function getAccountDetail(accountId: string): Promise<any> { | ||
| return new Promise((resolve, reject) => { | ||
| axios | ||
| .get(`https://stacks-node-api.testnet.stacks.co/v2/accounts/${accountId}`) | ||
| .then((res) => { | ||
| resolve(res ? res.data: ''); | ||
| }) | ||
| .catch((error) => { | ||
| reject(error); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| export async function getMicroBlock(): Promise<any> { | ||
| return new Promise((resolve, reject) => { | ||
| axios | ||
| .get('https://stacks-node-api.testnet.stacks.co/extended/v1/microblock/unanchored/txs') | ||
| .then((res) => { | ||
| resolve(res ? res.data: ''); | ||
| }) | ||
| .catch((error) => { | ||
| reject(error); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| export async function getAnchorBlock(): Promise<any> { | ||
| return new Promise((resolve, reject) => { | ||
| axios | ||
| .get('https://stacks-node-api.testnet.stacks.co/extended/v1/tx?limit=200') | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A block may contain more than 200 transactions, there could be cases where you don't see the transaction because of this paging limit. A better approach would be to record the current block_height before sending the tx, and then trying to retrieve the next blocks via
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please elaborate a bit on how to do this, this is my first time taking this approach and I want to make sure I clearly understand the steps. |
||
| .then((res) => { | ||
| resolve(res ? res.data: ''); | ||
| }) | ||
| .catch((error) => { | ||
| reject(error); | ||
| }); | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export const wait = async (ms: number) => { | ||
| return new Promise((resolve) => { | ||
| setTimeout(() => { | ||
| resolve(true); | ||
| }, ms); | ||
| }); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "target": "es5", | ||
| "module": "commonjs", | ||
| "lib": ["es6"], | ||
| "allowJs": true, | ||
| "outDir": "build", | ||
| "rootDir": "script", | ||
| "strict": true, | ||
| "noImplicitAny": false, | ||
| "esModuleInterop": true, | ||
| "resolveJsonModule": true | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There could be some situations when the transaction doesn't get picked up by any microblock but only by an anchor block.
I know you're currently not failing the test if you don't see the tx in a microblock, but I wanted to point that out in case you wanted to create a secondary test later surrounding microblocks.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a great point I can definitely create a better check for that I have an assertion for it currently but let me make sure it works accurately.