BSV Fundamentals
Module 2: Core BSV Blockchain Concepts
This module covers the core concepts and building blocks of the BSV blockchain. Understanding these fundamentals is essential for all BSV development.
Note: These concepts apply to both Backend and Frontend development paradigms. After completing this module, you'll choose your path:
Backend Development: Server-side wallet management → First Wallet (Backend)
Frontend Development: User wallet integration → WalletClient Integration
Learning Objectives
By the end of this module, you will understand:
How transactions and UTXOs work
Public/private key cryptography
Addresses and their derivation
Bitcoin Script basics
Satoshis and denominations
Block structure and confirmations
SPV and blockchain verification
Core Concepts
1. Transactions
Transactions are the fundamental unit of activity on the BSV blockchain.
Structure
interface Transaction {
version: number
inputs: TransactionInput[]
outputs: TransactionOutput[]
lockTime: number
}Key Points:
Transactions move value from inputs to outputs
Inputs reference previous outputs (UTXOs)
Outputs create new spendable UTXOs
Transaction ID (txid) is double SHA-256 hash
Example
import { Transaction } from '@bsv/sdk'
// Create a transaction
const tx = new Transaction()
// Add inputs (spending previous outputs)
tx.addInput(/* ... */)
// Add outputs (creating new spendable outputs)
tx.addOutput(/* ... */)2. UTXO Model
BSV uses the Unspent Transaction Output (UTXO) model, not an account model.
What is a UTXO?
A UTXO is:
An unspent output from a previous transaction
A discrete chunk of satoshis
Locked by a script (locking script)
Can only be spent once
UTXO vs Account Model
UTXO Model (BSV):
Alice's UTXOs:
- UTXO1: 50,000 sats (from tx abc...)
- UTXO2: 30,000 sats (from tx def...)
- UTXO3: 20,000 sats (from tx ghi...)
Total: 100,000 satsAccount Model (Ethereum):
Alice's Account:
- Balance: 100,000 satsBenefits of UTXO Model
Parallel Processing: UTXOs can be processed independently
Clear Ownership: Each UTXO has explicit owner
Atomic Transactions: All inputs spent or none
Efficient Verification: Can verify without full state
Working with UTXOs
// A UTXO reference
interface UTXO {
txid: string // Transaction ID
vout: number // Output index in that transaction
satoshis: number // Amount in satoshis
script: Script // Locking script
}
// To spend a UTXO, reference it as an input
const input = {
sourceTransaction: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScript: /* script to unlock */
}3. Public/Private Key Cryptography
BSV uses elliptic curve cryptography (specifically secp256k1).
Private Key
256-bit random number
Must be kept secret
Used to sign transactions
Controls access to funds
import { PrivateKey } from '@bsv/sdk'
// Generate random private key
const privateKey = PrivateKey.fromRandom()
// From WIF (Wallet Import Format)
const privateKey2 = PrivateKey.fromWif('L1234...')
// Never share your private key!Public Key
Derived from private key (one-way)
Can be shared publicly
Used to verify signatures
33 bytes (compressed) or 65 bytes (uncompressed)
// Derive public key from private key
const publicKey = privateKey.toPublicKey()
console.log(publicKey.toString()) // 02a1b2c3...Key Properties
One-way derivation: Private → Public (easy)
No reverse: Public → Private (impossible)
Signatures: Only private key can sign
Verification: Anyone with public key can verify
4. Addresses
Addresses are human-readable representations of public key hashes.
Address Derivation
Private Key
↓ (ECDSA)
Public Key
↓ (SHA-256)
SHA-256 Hash
↓ (RIPEMD-160)
Public Key Hash
↓ (Base58Check)
AddressCreating Addresses
const privateKey = PrivateKey.fromRandom()
const publicKey = privateKey.toPublicKey()
const address = publicKey.toAddress()
console.log(address) // 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNaAddress Formats
Legacy (P2PKH): Starts with
1(e.g.,1A1zP1eP...)Testnet: Starts with
morn
Important Notes
Addresses are not stored on blockchain
Only public key hashes are in locking scripts
Addresses are for human convenience
Multiple addresses can come from one public key
5. Satoshis and Denominations
Satoshi
The smallest unit of BSV:
1 BSV = 100,000,000 satoshis
Named after Satoshi Nakamoto
All amounts are integers (no decimals on blockchain)
Denominations
1 BSV = 100,000,000 satoshis
0.01 BSV = 1,000,000 satoshis (1 million)
0.0001 BSV = 10,000 satoshis
0.00000001 = 1 satoshi (smallest unit)In Code
// Always use satoshis in code
const amount = 100000000 // 1 BSV
const payment = 50000 // 0.0005 BSV
// Helper for conversion
function bsvToSatoshis(bsv: number): number {
return Math.round(bsv * 100000000)
}
function satoshisToBsv(sats: number): number {
return sats / 100000000
}Why Satoshis?
No floating point errors: Integer math is precise
Micropayments: Enable sub-cent transactions
Protocol native: Blockchain uses satoshis
6. Bitcoin Script
Bitcoin Script is a stack-based scripting language that defines spending conditions.
Locking Script (scriptPubKey)
Defines conditions to spend an output:
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIGUnlocking Script (scriptSig)
Provides data to satisfy locking script:
<signature> <publicKey>Script Execution
Scripts are concatenated and executed:
<signature> <publicKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIGExecution Steps:
Push signature to stack
Push publicKey to stack
OP_DUP duplicates publicKey
OP_HASH160 hashes publicKey
Push expected pubKeyHash
OP_EQUALVERIFY checks hash matches
OP_CHECKSIG verifies signature
If script completes without errors and top of stack is TRUE, output can be spent.
Script Features
Turing complete (with some limitations)
Stack-based: Uses a stack for operations
Deterministic: Same input = same output
Flexible: Can create complex conditions
import { Script } from '@bsv/sdk'
// Simple P2PKH locking script
const lockingScript = Script.fromASM(`
OP_DUP
OP_HASH160
${pubKeyHash}
OP_EQUALVERIFY
OP_CHECKSIG
`)7. Blocks and Confirmations
Block Structure
interface Block {
header: BlockHeader
transactions: Transaction[]
}
interface BlockHeader {
version: number
previousBlockHash: string
merkleRoot: string
timestamp: number
bits: number // Difficulty target
nonce: number // Proof of work
}Block Creation
Miners collect transactions from mempool
Build merkle tree of transaction IDs
Find nonce that satisfies difficulty
Broadcast block to network
Other nodes validate and extend chain
Confirmations
0 confirmations: In mempool, not in block (unconfirmed)
1 confirmation: Included in latest block
2 confirmations: One block built on top
6+ confirmations: Generally considered final
Confirmation Time
Average block time: ~10 minutes
1 confirmation: ~10 minutes
6 confirmations: ~1 hour
// Check confirmations
async function getConfirmations(txid: string): Promise<number> {
const txInfo = await fetchTransactionInfo(txid)
if (!txInfo.blockHeight) {
return 0 // Unconfirmed
}
const currentHeight = await getCurrentBlockHeight()
return currentHeight - txInfo.blockHeight + 1
}8. SPV (Simplified Payment Verification)
SPV allows lightweight clients to verify transactions without downloading the full blockchain.
How SPV Works
Download block headers only (~80 bytes each)
Request merkle proofs for specific transactions
Verify transaction is in block using merkle proof
Trust the chain with most proof-of-work
Merkle Proofs
Block Header
↓
Merkle Root
↓ (verify path)
Your TransactionBenefits
Lightweight: Only headers needed (~80 MB for 1M blocks)
Fast: Quick verification
Secure: Cryptographic proof of inclusion
Mobile-friendly: Works on resource-constrained devices
SPV vs Full Node
SPV Client:
Downloads headers only
Requests proofs as needed
Verifies specific transactions
Suitable for wallets
Full Node:
Downloads all blocks
Validates all transactions
Maintains full UTXO set
Suitable for miners, services
The example below demonstrates how SPV verification enables you to cryptographically prove that a transaction is included in a block without downloading the entire block. You only need the block header and a Merkle proof.
import { MerkleProof } from '@bsv/sdk'
// Verify a transaction is in a block using its merkle proof
// merkleProof: cryptographic proof from SPV server showing tx is in block
// blockHeader: 80-byte header containing merkle root to verify against
function verifyTxInBlock(
txid: string,
merkleProof: MerkleProof,
blockHeader: BlockHeader
): boolean {
// Calculate merkle root from the proof path
const calculatedRoot = merkleProof.calculateRoot(txid)
// Compare with the merkle root in the block header
// If they match, the transaction is proven to be in the block
return calculatedRoot === blockHeader.merkleRoot
}Putting It All Together
Transaction Lifecycle
Create Transaction
Select UTXOs to spend (inputs)
Create outputs for recipients
Create change output if needed
Sign Transaction
Create unlocking scripts with private key
Sign each input
Broadcast Transaction
Send to network via ARC or nodes
Transaction enters mempool
Mining
Miner includes transaction in block
Finds proof-of-work
Broadcasts block
Confirmation
Block added to longest chain
Each new block adds confirmation
After 6+ confirmations, considered final
Example: Complete Payment Flow
Important Note: This example shows the conceptual flow to understand how transactions work internally. In practice:
Frontend apps: Use
WalletClientwhich handles all of this automaticallyBackend apps: Use SDK's built-in methods for UTXO management, fees, and broadcasting
Conceptual Example (Understanding the Flow)
import { PrivateKey, Transaction, P2PKH } from '@bsv/sdk'
// This shows what happens "under the hood"
// You typically won't write this code yourself
async function sendPaymentConceptual(
privateKey: PrivateKey,
recipientAddress: string,
amount: number
) {
// 1. Get UTXOs (in practice, SDK or wallet handles this)
const myAddress = privateKey.toPublicKey().toAddress()
const utxos = await getUTXOs(myAddress)
// 2. Create transaction
const tx = new Transaction()
// 3. Add inputs (SDK can select UTXOs automatically)
let inputTotal = 0
for (const utxo of utxos) {
if (inputTotal >= amount + 1000) break // +fee
await tx.addInput({
sourceTransaction: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(privateKey)
})
inputTotal += utxo.satoshis
}
// 4. Add payment output
tx.addOutput({
satoshis: amount,
lockingScript: new P2PKH().lock(recipientAddress)
})
// 5. Add change output (SDK can calculate this automatically)
const fee = 500
const change = inputTotal - amount - fee
if (change > 0) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(myAddress)
})
}
// 6. Sign (SDK handles)
await tx.sign()
// 7. Broadcast (SDK/wallet handles)
const txid = await broadcast(tx)
return txid
}In Practice: Frontend with WalletClient
import { WalletClient } from '@bsv/sdk'
// What you actually write for frontend apps
async function sendPaymentFrontend(
wallet: WalletClient,
recipientAddress: string,
amount: number
) {
// Wallet handles: UTXO selection, fees, change, signing, broadcasting
const result = await wallet.createAction({
description: 'Send payment',
outputs: [{
lockingScript: new P2PKH().lock(recipientAddress).toHex(),
satoshis: amount,
outputDescription: 'Payment'
}]
})
return result.txid
}In Practice: Backend with SDK
import { PrivateKey, Transaction, P2PKH } from '@bsv/sdk'
// What you write for backend services (simplified)
async function sendPaymentBackend(
privateKey: PrivateKey,
recipientAddress: string,
amount: number
) {
const tx = new Transaction()
// Add payment output
tx.addOutput({
satoshis: amount,
lockingScript: new P2PKH().lock(recipientAddress)
})
// SDK handles: UTXO selection, fee calculation, change outputs
await tx.sign(privateKey)
const txid = await tx.broadcast()
return txid
}Key Point: The detailed manual code above is for understanding the concepts. In real applications, the SDK and WalletClient handle UTXO selection, fee calculation, change outputs, and broadcasting automatically.
Key Takeaways
✅ Transactions move value via inputs and outputs
✅ UTXO Model enables parallel processing and clear ownership
✅ Private Keys must be kept secret and control funds
✅ Public Keys are derived from private keys and can be shared
✅ Addresses are Base58-encoded public key hashes
✅ Satoshis are the base unit (100M sats = 1 BSV)
✅ Bitcoin Script defines spending conditions
✅ Blocks contain transactions and build on previous blocks
✅ Confirmations indicate transaction finality
✅ SPV enables lightweight verification
Practice Exercises
Generate Keys: Create a private key, derive public key and address
Calculate Amounts: Convert between BSV and satoshis
Understand UTXOs: Trace how UTXOs are created and spent
Read Scripts: Interpret a P2PKH locking script
Track Confirmations: Monitor a transaction from broadcast to 6 confirmations
Related Components
Next Steps
Now that you understand BSV fundamentals, choose your development path:
Backend Development (Custodial)
You control private keys server-side
Continue to: Your First Wallet (Backend) - Learn server-side wallet management, UTXO tracking, and programmatic transaction creation.
Frontend Development (Non-Custodial)
Users control their own keys via wallet
Continue to: WalletClient Integration - Learn to connect to MetaNet Desktop Wallet and request user signatures.
Not sure which path? Review Development Paradigms to understand the differences.
Additional Resources
Mastering Bitcoin - General Bitcoin concepts
Last updated
