Monday, May 20, 2024
No menu items!
HomeDatabase ManagementList unspent transaction outputs by address on Bitcoin with Amazon Managed Blockchain...

List unspent transaction outputs by address on Bitcoin with Amazon Managed Blockchain Query

In order to build an application that interacts with the Bitcoin blockchain, whether it be a wallet, an Ordinals marketplace, or a BTC exchange, you must be able to reliably access the Bitcoin network. For example, you will need to read critical data from the blockchain that acts as input for properly constructed Bitcoin transactions. The most foundational of these data needs is retrieving data pertaining to transaction outputs for a given wallet from the Bitcoin network, which serves a variety of use cases. For example, retrieving unspent transaction outputs (UTXOs) to be consumed as inputs for outgoing transactions of BTC, retrieving all UTXOs for a given wallet to calculate a balance in BTC, gathering spent outputs to render a list of past Bitcoin transactions, and more. There are multiple options for retrieving this data, including third-party data providers and node operators or running your own Bitcoin Core node.

Operating your own Bitcoin Core node prioritizes independence and verifiability, and allows you to track transaction output data for a limited set of wallets (comprising n number of addresses). However, using self-managed Bitcoin Core nodes as is limits the scale at which you can retrieve transaction output data. In contrast, Amazon Managed Blockchain (AMB) uses cloud scale indexing and storage to provide a low-latency API for data pertaining to an unlimited set of wallets. AMB Query offers convenience, ease of scale, and pricing efficiency for Bitcoin workloads, helping you shed the undifferentiated heavy lifting of managing Bitcoin nodes and performing data extractions from the network at scale.

In this post, we focus on how to use the blockchain data APIs available in AMB Query to provide reliable, cost-effective access to Bitcoin data at scale, including transaction output data. Furthermore, AMB Access provides serverless access to a fleet of Bitcoin nodes that you can use to broadcast your Bitcoin transactions after you have retrieved UTXOs ready to spend from AMB Query.

AMB Query and the ListFilteredTransactionEvents API

AMB Query provides a set of developer-friendly APIs that serve current and historical blockchain data from multiple public blockchains, including Bitcoin. With AMB Query, developers can shed the undifferentiated heavy lifting of node operations, blockchain data indexing, and ETL (extract, transform, and load), and focus their efforts on differentiated features for their application. With AMB Query, you can now use the ListFilteredTransactionEvents API to query for spent or unspent transaction outputs for an address or set of addresses on the Bitcoin network and its testnet, providing an alternative to the built-in listunspent JSON-RPC API in the Bitcoin Core node implementation.

The ListFilteredTransactionEvents API allows developers to do the following:

Retrieve unspent transaction outputs for a given address or set of addresses to ease the process of constructing Bitcoin transactions
Retrieve spent transaction output information to populate dashboards or wallet interfaces
Filter transaction events pertaining to unspent outputs by timestamp and finality status

Before demonstrating ListFilteredTransactionEvents functionality in a code sample, it’s important to disambiguate the fundamentals of the unspent transaction output (UTXO) model employed by Bitcoin and why you will inevitably require an API like ListFilteredTransactionEvents when building a Bitcoin application.

Understanding the unspent transaction output (UTXO) model

The Bitcoin blockchain uses the UTXO accounting method, so tasks such as calculating a spendable balance of BTC or populating a transaction to spend BTC begin with compiling the unspent transaction outputs for a given wallet. In Bitcoin, each new transaction of BTC consumes unspent outputs from previous transactions, and in turn results in new unspent outputs to be consumed by future transactions. This concept is best illustrated by analyzing the anatomy of a Bitcoin transaction:

{
“txid” : “c80b343d2ce2b5d829c2de9854c7c8d423c0e33bda264c4013
8d834aab4c0638″,
“hash” : “c80b343d2ce2b5d829c2de9854c7c8d423c0e33bda264c40138d834aab4c0638”,
“size” : 85,
“vsize” : 85,
“version” : 1,
“locktime” : 0,
“vin” : [
{
“txid” : “3f4fa19803dec4d6a84fae3821da7ac7577080ef75
451294e71f9b20e0ab1e7b”,
“vout” : 0,
“scriptSig” : {
“asm” : “”,
“hex” : “”
},
“sequence” : 4294967295
}
],
“vout” : [
{
“value” : 49.99990000,
“n” : 0,
“scriptPubKey” : {
“asm” : “OP_DUP OP_HASH160 cbc20a7664f2f69e5355a
a427045bc15e7c6c772 OP_EQUALVERIFY OP_CHECKSIG”,
“hex” : “76a914cbc20a7664f2f69e5355aa427045bc15e
7c6c77288ac”,
“reqSigs” : 1,
“type” : “pubkeyhash”,
“addresses” : [
“mz6KvC4aoUeo6wSxtiVQTo7FDwPnkp6URG”
]
}
}
]
}

The vin object defines the value (BTC) going into the transaction, which refers to one or more of the previous vout objects. These vout objects are the unspent value out from previous Bitcoin transactions. Each new Bitcoin transaction will take unspent transaction outputs from previous transactions in the vin object, and then define new vout entries to spend (send) them to other wallets.

The vout array defines the list of outputs for the transaction, including the recipient, value (in BTC), and more. The total value of the vout entries will generally be equal to the sum of the value of vin, and will often define change as a vout to return to the sender of the transaction. This results in new unspent outputs (vout) for the sender to spend in another transaction. Although the preceding transaction example uses a single vin and vout, it is common for each transaction to have multiple vin and vout entries defined.

As you can infer from the construction of a Bitcoin transaction, calculating the total spendable balance of a given wallet requires calculating the sum of the total unspent transaction outputs (vout) for a given wallet. In the absence of an API to provide this insight, you would have to make a large quantity of requests to a Bitcoin node to retrieve all vin and vout within transactions pertaining to your wallet and perform calculations to determine which are spent or unspent outputs. This information is also a prerequisite to constructing a valid Bitcoin transaction. With AMB Query, it is straightforward to retrieve a list of spent and unspent transaction outputs for a given set of addresses, which is illustrated in the next section.

How to use AMB Query to get a list of unspent transaction outputs

The ListFilteredTransactionEvents API takes a list of addresses and returns any applicable Bitcoin transaction VOUT events, both spent and unspent, pertaining to those addresses. Data is returned in the following format, which provides the key properties that you may use to populate a Bitcoin transaction with any number of those unspent outputs:

HTTP/1.1 200
Content-type: application/json

{
“events”: [
{
“blockchainInstant”: {
“time”: number
},
“confirmationStatus”: “string”,
“contractAddress”: “string”,
“eventType”: “string”,
“from”: “string”,
“network”: “string”,
“spentVoutIndex”: number,
“spentVoutTransactionHash”: “string”,
“spentVoutTransactionId”: “string”,
“to”: “string”,
“tokenId”: “string”,
“transactionHash”: “string”,
“transactionId”: “string”,
“value”: “string”,
“voutIndex”: number,
“voutSpent”: boolean
}
],
“nextToken”: “string”
}

In the preceding response format document, you will find several properties pertaining to spent and unspent transaction outputs. When retrieving spent transaction outputs, spentVoutTransactionHash, spentVoutTransactionId, and spentVoutIndex define the transaction identifiers and the array position of the spent output (vout) so you can easily determine the origin of that output. For unspent transaction outputs, the properties transactionHash, transactionId, and voutIndex define the transaction identifiers and the array position of the unspent output in the parent transaction. Additionally, the flag confirmationStatus defines whether the transaction containing the output is considered final on the Bitcoin blockchain. In AMB Query, a transaction’s finality is defined as equal to or greater than six confirmations, or blocks mined after the block in which the transaction is contained. Transactions considered final according to this criterion will be marked FINAL, whereas unconfirmed transactions will be marked NONFINAL.

To better illustrate the response from the API, the JavaScript code sample defines a simple script to retrieve a list of unspent transaction outputs for a single Bitcoin address on the Bitcoin testnet. To run these Node.js examples, the following prerequisites apply:

You must have node version manager (nvm) and Node.js installed on your machine. You can find installation instructions for your OS in the nvm GitHub repo.
Use the node –version command and confirm that you are using Node version v18.12.0 (LTS) or higher. If required, you can use the nvm install 18.12.0 command, followed by the nvm use 18.12.0 command to install.
The environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must contain the credentials that are associated with the account. Export these variables as strings on your client by using the following commands. Replace the values in the following code with appropriate values from the AWS Identity and Access Management (IAM) user account.

Copy the following two code snippets to your local working directory, containing the code sample (index.js) and package.json, which contains one dependency, the official AWS SDK module for AMB Query:

The following is the package.json code:

{
“name”: “query-bitcoin”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1″
},
“author”: “”,
“license”: “ISC”,
“dependencies”: {
“@aws-sdk/client-managedblockchain-query”: “^3.538.0”
}
}

The following is the index.js code:

const { ManagedBlockchainQueryClient, ListFilteredTransactionEventsCommand } = require(“@aws-sdk/client-managedblockchain-query”);

const listUnspentOutputs = async (addresses) => {
const client = new ManagedBlockchainQueryClient({ region: “us-east-1” });

const input = { // ListFilteredTransactionEventsInput
addressIdentifierFilter: {
“transactionEventToAddress”: [ …addresses ]
}, // list addresses to get events for
network: “BITCOIN_TESTNET”, // define the network, testnet or mainnet
sort: { // sort order
sortOrder: “ASCENDING”,
},
maxResults: Number(“250”),
“voutFilter”: {
“voutSpent”: false // retrieve only unspent outputs
}
};

const command = new ListFilteredTransactionEventsCommand(input);

try {
const data = await client.send(command);
console.log(data);
} catch (error) {
console.error(error);
}
}

listUnspentOutputs([‘tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t’]) // input the Bitcoin addresses of your choice here

To use this code, run the following command from your working directory, replacing the address defined in the index.js with the addresses of your choice, if desired:

npm i && node index.js

With the filter set for unspent transactions only in the preceding code sample, the result will be similar to the following:

events: [
{
blockchainInstant: [Object],
confirmationStatus: ‘FINAL’,
eventType: ‘BITCOIN_VOUT’,
network: ‘BITCOIN_TESTNET’,
to: ‘tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t’,
tokenId: ‘btc’,
transactionHash: ‘195d8c08bda48315b4ba3690264dcee36a7862f98c15d3dde9cf57bbbdf6c6ad’,
transactionId: ‘438961c65220ca0c9427614f9dcd2a7ca8a1e638e7702debae6aa28417045ce7’,
value: ‘0.00001’,
voutIndex: 0,
voutSpent: false
},
{
blockchainInstant: [Object],
confirmationStatus: ‘FINAL’,
eventType: ‘BITCOIN_VOUT’,
network: ‘BITCOIN_TESTNET’,
to: ‘tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t’,
tokenId: ‘btc’,
transactionHash: ‘c1f2b279e50142fbbf62282e51c3e0a2f04247173f3b9dceef15345e12aa5db2’,
transactionId: ‘0ec51620a146b9d3e61b35fd68c7fa3e124a1b4bd2d582177fe9529a8c59158b’,
value: ‘0.00001’,
voutIndex: 0,
voutSpent: false
},
{
blockchainInstant: [Object],
confirmationStatus: ‘FINAL’,
eventType: ‘BITCOIN_VOUT’,
network: ‘BITCOIN_TESTNET’,
to: ‘tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t’,
tokenId: ‘btc’,
transactionHash: ‘347994d54743989fb65e8845d6d10b8cea87b0d4aa6c98dfcdd063735711be8a’,
transactionId: ‘8ce1bbf94989cc23a13d5bf2d44dbba51b2f50a4ab759cf1309e97a3eb7d5b76’,
value: ‘0.00001’,
voutIndex: 0,
voutSpent: false
},
{
blockchainInstant: [Object],
confirmationStatus: ‘FINAL’,
eventType: ‘BITCOIN_VOUT’,
network: ‘BITCOIN_TESTNET’,
to: ‘tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t’,
tokenId: ‘btc’,
transactionHash: ‘d307359bbc4d68542f2c9c2bce73094280ad304b7240715305fcb3f80323308d’,
transactionId: ’17ae7be33a27ab9bee81b91428abca423824e68be8e944399809a5f0fe197c90′,
value: ‘0.00001545’,
voutIndex: 0,
voutSpent: false
}
]
}

In the response, you can see that the transaction identifiers (transactionHash and transactionId), value, and voutIndex are defined for each unspent output, which can serve as inputs for constructing future Bitcoin transactions.

You can modify the filters in your query to retrieve different data, such as retrieving both unspent and spent outputs, or restricting the timespan for which to retrieve data. For more information about these filters, refer to Request Body. Requests to AMB Query APIs are billed per request based on pricing buckets per million requests. You can view the latest pricing information for each API to calculate the cost of requests to AMB Query.

Conclusion

In this post, we outlined the fundamentals of the UTXO model made prevalent by Bitcoin, how UTXOs are central to transactions in Bitcoin, and how to retrieve UTXO data using AMB Query. To learn more about AMB Query, see What is Amazon Managed Blockchain (AMB) Query? To learn more about other Managed Blockchain offerings, see Amazon Managed Blockchain.

About the author

Forrest Colyer manages the Web3/Blockchain Specialist Solutions Architecture team that supports the Amazon Managed Blockchain (AMB) service. Forrest and his team support customers at every stage of their adoption journey, from proof of concept to production, providing deep technical expertise and strategic guidance to help bring blockchain workloads to life. Through his experience with private blockchain solutions led by consortia and public blockchain use cases like NFTs and DeFi, Forrest helps enable customers to identify and implement high-impact blockchain solutions.

Read MoreAWS Database Blog

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments