Programming with Bitcoin Blocks and Transactions
Rooch incorporates the bitcoin-move (opens in a new tab) framework, which allows developers to read Bitcoin blocks and transactions through the bitcoin-move framework and use the data carried in them for programming.
Reading Bitcoin Blocks and Transactions in Contracts
In Rooch, Bitcoin blocks and transactions are written into the global state of Rooch by a Relayer. All states are stored in 0x4::bitcoin::BitcoinBlockStore
. Developers can read Bitcoin block and transaction data through the interfaces provided by the 0x4::bitcoin
module.
1. BitcoinBlockStore
Structure and Reading Interfaces
The BitcoinBlockStore
structure stores all block headers and transaction data. Its fields are as follows:
latest_block_height
: Optionalu64
, representing the height of the latest block.blocks
:Table<address, Header>
, storing the mapping from block hash to block header. Developers can read the corresponding block header by block hash.height_to_hash
:Table<u64, address>
, storing the mapping from block height to block hash.hash_to_height
:Table<address, u64>
, storing the mapping from block hash to block height.txs
:Table<address, Transaction>
, storing the mapping from transaction hash to transaction.tx_ids
:TableVec<address>
, storing all transaction hashes.
The BitcoinBlockStore
is stored in a shared object, and anyone can read the block and transaction data stored in the BitcoinBlockStore
object through the interfaces provided in the 0x4::bitcoin
module.
The specific interfaces are as follows:
-
get_tx
: Get the transaction with the specified transaction hash.-
Parameters
txid
:address
, representing the transaction hash.
-
Return Value
Option<Transaction>
: If the transaction exists, return the transaction; otherwise, return none.
-
-
get_block
: Get the block header with the specified block hash.-
Parameters
block_hash
:address
, representing the block hash.
-
Return Value
Option<Header>
: If the block exists, return the block header; otherwise, return none.
-
-
get_block_height
: Get the block height with the specified block hash.-
Parameters
block_hash
:address
, representing the block hash.
-
Return Value
Option<u64>
: If the block exists, return its block height; otherwise, return none.
-
-
get_block_by_height
: Get the block header with the specified block height.-
Parameters
block_height
:u64
, representing the block height.
-
Return Value
Option<Header>
: If the block exists, return the block header; otherwise, return none.
-
-
get_latest_block
: Get the height and hash of the latest block.-
Return Value
Option<BlockHeightHash>
: If the latest block exists, return the block height; otherwise, return none.
-
2. Header
Structure and Reading Interfaces
The Header
structure represents the Bitcoin block header, used to store the metadata of the block, including the version number, the hash of the previous block, the root hash of the Merkle tree, the timestamp, the target value, and the nonce. In Rooch, the Header can be read through the 0x4::bitcoin::get_block
and 0x4::bitcoin::get_block_by_height
interfaces.
Its field definitions are as follows:
version
:u32
, representing the version number of the block, currently used for signaling soft forks.prev_blockhash
:address
, representing the hash of the previous block.merkle_root
:address
, representing the root hash of the Merkle tree of transactions in the block.time
:u32
, representing the timestamp of the block, provided by the miner.bits
:u32
, representing the target value of the block, the block hash must be lower than this value.nonce
:u32
, representing the chosen nonce to obtain a sufficiently low block hash.
The corresponding reading interfaces are defined in the 0x4::types
module.
3. Transaction
Structure and Reading Interfaces
The Transaction
structure represents a Bitcoin transaction, used to store the metadata of the transaction, including the version number, inputs, outputs, lock time, and transaction hash. In Rooch, the Transaction can be read through the 0x4::bitcoin::get_tx
interface.
Its field definitions are as follows:
id
:address
, representing the unique identifier (txid) of the transaction.version
:u32
, representing the protocol version of the transaction, currently expected to be 1 or 2 (BIP 68).lock_time
:u32
, representing the locked block height or timestamp, before which the transaction cannot be included in a block.input
:vector<TxIn>
, representing the list of transaction inputs.output
:vector<TxOut>
, representing the list of transaction outputs.
TxIn
represents an input in a Bitcoin transaction. It contains a reference to an output of a previous transaction, a script signature, a sequence number, and witness data. The fields are as follows:
previous_output
:OutPoint
, representing a reference to the output of a previous transaction.script_sig
:vector<u8>
, representing the script signature, which pushes values onto the stack to make the referenced output script be accepted.sequence
:u32
, representing the sequence number, which suggests to miners which of two conflicting transactions to choose, or set to 0xFFFFFFFF to ignore this feature. This feature is usually not used because miner behavior cannot be enforced.witness
:Witness
, representing the witness data, which is an array of byte arrays.
The TxIn
structure is a key component of Bitcoin transactions, defining the source of transaction inputs. The previous_output
field references an output of a previous transaction, the script_sig
field contains data for verifying the referenced output script, the sequence
field is used to specify the order of transactions, and the witness
field contains witness data supporting certain types of transactions.
TxOut
represents an output in a Bitcoin transaction. It contains the amount of the output and the script. The fields are as follows:
value
:u64
, representing the amount of the output, in satoshis.script_pubkey
:vector<u8>
, representing the output script, which defines how the recipient proves they own the funds.recipient_address
:BitcoinAddress
, representing the recipient address of the output. If known, this will be a valid Bitcoin address; if unknown, the address bytes will be empty.
The reading interfaces for the fields of Transaction
, TxIn
, and TxOut
are defined in the 0x4::types
module.
TODO: This section of the document needs improvement
- An explanation of how Bitcoin hashes are expressed and differ in Move is required.
Application Scenarios
- Use a Bitcoin hash as a random number seed to implement a random number generator. Note, this scenario requires using the hash of future blocks to prevent predictability.
- Embed application data in Bitcoin via OP_RETURN, then read the OP_RETURN data from the transaction using a Move contract for processing.
- Validate Bitcoin transaction scripts in Move to enable mixed programming with Move and Bitcoin Script. This feature is still in development, and progress can be tracked at Issue #1651 (opens in a new tab).
Example
- btc_blind_box (opens in a new tab) A simple blind box opening example that uses Bitcoin block hashes as the random number seed, implementing the functionality of opening blind boxes. The process of claiming a blind box is divided into two stages: 1. Application stage; 2. Claiming stage. During the application stage, players can apply for a blind box and receive a random magic number. After the application period ends, all players' magic numbers become unchangeable. Then, after a certain interval, the claiming stage begins, where the latest Bitcoin block header is used as the random seed, combined with the player's magic number to generate a random number, determining the rarity of the blind box the player receives. This ensures sufficient randomness; at the same time, during the application stage, players cannot predict the information of future block headers, thus ensuring unpredictability.