BTC

基础

Block chain is a linked list using hash pointers.

哈希

签名

asymmetric encryption algorithm
sign with private key, verify with public key

哈希指针

协议

实现

网络

分叉

数据结构

比特币源码分析-交易 - 知乎

一笔尚未被打包进区块、但已完全序列化好的 比特币原始交易

{
    "version": 1,
    "locktime": 0,
    "vin": [
        {
            "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
            "vout": 0,
            "scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
            "sequence": 4294967295
        }
    ],
    "vout": [
        {
            "value": 0.015,
            "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
        },
        {
            "value": 0.0845,
            "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
        }
    ]
}

一些要注意的细节

实现

javascript

generated by Claude Code

const crypto = require('crypto');

class Block {
    constructor(timestamp, transactions, previousHash = '') {
        this.timestamp = timestamp;
        this.transactions = transactions;
        this.previousHash = previousHash;
        this.nonce = 0;
        this.hash = this.calculateHash();
    }

    calculateHash() {
        return crypto
            .createHash('sha256')
            .update(this.previousHash + this.timestamp + JSON.stringify(this.transactions) + this.nonce)
            .digest('hex');
    }

    mineBlock(difficulty) {
        const target = Array(difficulty + 1).join('0');
        
        while (this.hash.substring(0, difficulty) !== target) {
            this.nonce++;
            this.hash = this.calculateHash();
        }
        
        console.log(`Block mined: ${this.hash}`);
    }
}

class Transaction {
    constructor(fromAddress, toAddress, amount) {
        this.fromAddress = fromAddress;
        this.toAddress = toAddress;
        this.amount = amount;
        this.timestamp = Date.now();
    }
}

class Blockchain {
    constructor() {
        this.chain = [this.createGenesisBlock()];
        this.difficulty = 2;
        this.pendingTransactions = [];
        this.miningReward = 100;
    }

    createGenesisBlock() {
        return new Block(Date.parse('2024-01-01'), [], '0');
    }

    getLatestBlock() {
        return this.chain[this.chain.length - 1];
    }

    minePendingTransactions(miningRewardAddress) {
        const rewardTransaction = new Transaction(null, miningRewardAddress, this.miningReward);
        this.pendingTransactions.push(rewardTransaction);

        let block = new Block(Date.now(), this.pendingTransactions, this.getLatestBlock().hash);
        block.mineBlock(this.difficulty);

        console.log('Block successfully mined!');
        this.chain.push(block);

        this.pendingTransactions = [];
    }

    createTransaction(transaction) {
        if (!transaction.fromAddress || !transaction.toAddress) {
            throw new Error('Transaction must include from and to address');
        }

        if (!this.isValidTransaction(transaction)) {
            throw new Error('Invalid transaction: insufficient balance');
        }

        this.pendingTransactions.push(transaction);
    }

    getBalance(address) {
        let balance = 0;

        for (const block of this.chain) {
            for (const trans of block.transactions) {
                if (trans.fromAddress === address) {
                    balance -= trans.amount;
                }

                if (trans.toAddress === address) {
                    balance += trans.amount;
                }
            }
        }

        return balance;
    }

    isValidTransaction(transaction) {
        if (transaction.fromAddress === null) return true;
        
        return this.getBalance(transaction.fromAddress) >= transaction.amount;
    }

    getAllTransactionsForWallet(address) {
        const txs = [];

        for (const block of this.chain) {
            for (const tx of block.transactions) {
                if (tx.fromAddress === address || tx.toAddress === address) {
                    txs.push(tx);
                }
            }
        }

        return txs;
    }

    isChainValid() {
        const realGenesis = JSON.stringify(this.createGenesisBlock());

        if (realGenesis !== JSON.stringify(this.chain[0])) {
            return false;
        }

        for (let i = 1; i < this.chain.length; i++) {
            const currentBlock = this.chain[i];
            const previousBlock = this.chain[i - 1];

            if (!currentBlock.hasValidTransactions()) {
                return false;
            }

            if (currentBlock.hash !== currentBlock.calculateHash()) {
                return false;
            }

            if (currentBlock.previousHash !== previousBlock.hash) {
                return false;
            }
        }

        return true;
    }
}

Block.prototype.hasValidTransactions = function() {
    for (const tx of this.transactions) {
        if (!tx.fromAddress || !tx.toAddress) continue;
    }
    return true;
};

module.exports = { Blockchain, Transaction };
const crypto = require('crypto');
const { Transaction } = require('./basic-btc-blockchain');

class Wallet {
    constructor() {
        this.keyPair = crypto.generateKeyPairSync('rsa', {
            modulusLength: 2048,
            publicKeyEncoding: {
                type: 'spki',
                format: 'pem'
            },
            privateKeyEncoding: {
                type: 'pkcs8',
                format: 'pem'
            }
        });
        
        this.publicKey = this.keyPair.publicKey;
        this.privateKey = this.keyPair.privateKey;
    }

    getAddress() {
        return crypto.createHash('sha256')
            .update(this.publicKey)
            .digest('hex')
            .substring(0, 40);
    }

    signTransaction(transaction) {
        if (transaction.fromAddress !== this.getAddress()) {
            throw new Error('You cannot sign transactions for other wallets!');
        }

        const hashTx = crypto.createHash('sha256')
            .update(JSON.stringify(transaction))
            .digest('hex');

        const sign = crypto.createSign('SHA256');
        sign.update(hashTx);
        sign.end();

        return sign.sign(this.privateKey, 'hex');
    }

    createTransaction(toAddress, amount, blockchain) {
        if (amount <= 0) {
            throw new Error('Transaction amount must be greater than 0');
        }

        if (blockchain.getBalance(this.getAddress()) < amount) {
            throw new Error('Insufficient balance');
        }

        const transaction = new Transaction(this.getAddress(), toAddress, amount);
        transaction.signature = this.signTransaction(transaction);
        
        return transaction;
    }

    static verifyTransaction(transaction) {
        if (transaction.fromAddress === null) return true;

        if (!transaction.signature || transaction.signature.length === 0) {
            throw new Error('No signature in this transaction');
        }

        const publicKey = crypto.createPublicKey({
            key: Buffer.from(`-----BEGIN PUBLIC KEY-----\n${transaction.fromAddress}\n-----END PUBLIC KEY-----`, 'utf8'),
            format: 'pem'
        });

        const hashTx = crypto.createHash('sha256')
            .update(JSON.stringify({
                fromAddress: transaction.fromAddress,
                toAddress: transaction.toAddress,
                amount: transaction.amount,
                timestamp: transaction.timestamp
            }))
            .digest('hex');

        const verify = crypto.createVerify('SHA256');
        verify.update(hashTx);
        verify.end();

        return verify.verify(publicKey, transaction.signature, 'hex');
    }
}

module.exports = Wallet;
const { Blockchain, Transaction } = require('./basic-btc-blockchain');
const Wallet = require('./wallet');

console.log('🚀 Starting Bitcoin Blockchain Demo...\n');

const myCoin = new Blockchain();

const wallet1 = new Wallet();
const wallet2 = new Wallet();

console.log(`👛 Wallet 1 Address: ${wallet1.getAddress()}`);
console.log(`👛 Wallet 2 Address: ${wallet2.getAddress()}`);

console.log('\n⛏️  Mining first block...');
myCoin.minePendingTransactions(wallet1.getAddress());

console.log(`\n💰 Balance of Wallet 1: ${myCoin.getBalance(wallet1.getAddress())}`);
console.log(`💰 Balance of Wallet 2: ${myCoin.getBalance(wallet2.getAddress())}`);

console.log('\n💸 Creating transaction from Wallet 1 to Wallet 2...');
try {
    const tx1 = wallet1.createTransaction(wallet2.getAddress(), 50, myCoin);
    myCoin.createTransaction(tx1);
    console.log('✅ Transaction created successfully!');
} catch (error) {
    console.log('❌ Error creating transaction:', error.message);
}

console.log('\n⛏️  Mining pending transactions...');
myCoin.minePendingTransactions(wallet2.getAddress());

console.log(`\n💰 Balance of Wallet 1: ${myCoin.getBalance(wallet1.getAddress())}`);
console.log(`💰 Balance of Wallet 2: ${myCoin.getBalance(wallet2.getAddress())}`);

console.log('\n📊 Transaction history for Wallet 1:');
const wallet1Transactions = myCoin.getAllTransactionsForWallet(wallet1.getAddress());
wallet1Transactions.forEach((tx, index) => {
    console.log(`  ${index + 1}. ${tx.fromAddress ? 'Sent' : 'Received'} ${tx.amount} BTC to ${tx.toAddress}`);
});

console.log('\n🔗 Blockchain validation...');
console.log(`Is blockchain valid? ${myCoin.isChainValid() ? '✅ Yes' : '❌ No'}`);

console.log('\n📋 Complete blockchain:');
myCoin.chain.forEach((block, index) => {
    console.log(`\nBlock ${index}:`);
    console.log(`  Hash: ${block.hash}`);
    console.log(`  Previous Hash: ${block.previousHash}`);
    console.log(`  Timestamp: ${new Date(block.timestamp).toLocaleString()}`);
    console.log(`  Transactions: ${block.transactions.length}`);
    console.log(`  Nonce: ${block.nonce}`);
});

console.log('\n🎯 Demo completed!');

java

仿BitCoin架构

ETH

特点

账户

  1. externally owned account
    • balance
    • nonce(交易次数)
  2. smart contract account
    • balance
    • nonce(创建过的合约数量)
    • code
    • storage

数据结构

https://github.com/ethereum/go-ethereum

https://learnblockchain.cn/2018/01/04/understanding-smart-contracts

以太坊 Merkle Patricia Tree 全解析 - 知乎

状态树

tx-based ledger中,通过merkle tree存储所有的交易,就可以变相存储每个地址的余额。account-based ledger中,如何让每个节点都能确认每个账户的余额?

MPT的Merkle Proof

以上图为例,我想证明叶节点{'a77d397':0.12ETH}存在,Merkle Proof只需要提供路径上的所有经过的中间节点,验证者收到后要做几件事:

  1. 计算叶节点{'a77d397':0.12ETH}的哈希H(leaf1)
  2. 比较H(leaf1)是否与上一Branch Node的槽9中值一致
  3. 计算分支节点的哈希H(branch1)
  4. 比较H(branch1)是否与上一个扩展节点的next node槽中的值一致
  5. 计算扩展节点的哈希H(ex1)
  6. 以此类推......
  7. 计算根节点哈希并与自己手上的根哈希比较,最后验证了该账户存在。

交易树和收据树

交易树只证明“发生了什么”,收据树才证明“结果是什么”。

轻节点想要查询某个交易/某种交易,需要使用布隆过滤器
先查询块头的Bloom Filter,把确定没有的块排除,再查询每个块的收据树的Bloom Filter.

交易树没有Bloom 结构

// NewBlock creates a new block. The input data is copied, changes to header and to the
// field values will not affect the block.
//
// The body elements and the receipts are used to recompute and overwrite the
// relevant portions of the header.
//
// The receipt's bloom must already calculated for the block's bloom to be
// correctly calculated.
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block {
	if body == nil {
		body = &Body{}
	}
	var (
		b           = NewBlockWithHeader(header)
		txs         = body.Transactions
		uncles      = body.Uncles
		withdrawals = body.Withdrawals
	)
	
	// 创建交易树 交易列表
	// 如果没有交易就返回空哈希
	if len(txs) == 0 {
		b.header.TxHash = EmptyTxsHash
	} else {
		// 交易树的根哈希
		b.header.TxHash = DeriveSha(Transactions(txs), hasher)
		// 创建交易列表
		b.transactions = make(Transactions, len(txs))
		copy(b.transactions, txs)
	}
	// 创建收据树
	if len(receipts) == 0 {
		b.header.ReceiptHash = EmptyReceiptsHash
	} else {
		b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
		// Receipts must go through MakeReceipt to calculate the receipt's bloom
		// already. Merge the receipt's bloom together instead of recalculating
		// everything.
		// 创建块头的BloomFilter
		b.header.Bloom = MergeBloom(receipts)
	}

	// 构建叔父区块
	if len(uncles) == 0 {
		b.header.UncleHash = EmptyUncleHash
	} else {
		b.header.UncleHash = CalcUncleHash(uncles)
		b.uncles = make([]*Header, len(uncles))
		for i := range uncles {
			b.uncles[i] = CopyHeader(uncles[i])
		}
	}

	if withdrawals == nil {
		b.header.WithdrawalsHash = nil
	} else if len(withdrawals) == 0 {
		b.header.WithdrawalsHash = &EmptyWithdrawalsHash
		b.withdrawals = Withdrawals{}
	} else {
		hash := DeriveSha(Withdrawals(withdrawals), hasher)
		b.header.WithdrawalsHash = &hash
		b.withdrawals = slices.Clone(withdrawals)
	}

	return b
}

共识机制:Ghost Protocol

ETH出块速度太快,分支太多,mining centralization影响严重。

GHOST v1:非最长链的2个前区块为叔父区块,能获得7/8的奖励。子区块记录叔父区块也能获得1/32的奖励。

算法

目标:防止挖矿设备专业化ASIC resistance,所以要memory hard,同时也要easy to verify
Ethash :改进版的scrypt
16M cache+1G DAG.

难度调整

权益证明

也叫Virtual Mining。
POS才是内循环的,不会被外部资源所影响。但是需要proof of deposit.

如何从POW转向POS?社区希望在不抛弃 PoW 的前提下,引入 PoS 的“最终性”机制,为后续全面 PoS(信标链)做演练。于是提出 Casper FFG——一个“附加在 PoW 上的最终性小工具(gadget)”

Casper the Friendly Finality Gadget(FFG)

流程
奖惩

智能合约