这个事情还要从数字化人民币说起,刚开始很多人对所谓数字化人民币可以在没有网络的情况下交易感到非常神奇,虽然我没有使用过数字化人民币,但是我比较清楚这个或多或少是种片面的误解,而且其所使用的技术根本超越不了比特币,可谓是小巫见大巫。
网络===互联网?
如果这里的网络是指互联网,比特币交易自然不会依赖于互联网,因为它可以通过无线电或卫星发送交易: 网络===所有连接方式?
如果你是说这些无线电啥的全都没有呢,完全离线,空气隔绝,这个要分情况,首先如果是使用单个设备做转账,你啥都不连,impossible!
但如果是两个设备之间做交易,那其中一台离线是可以的,这就是比特币冷钱包的玩法了,完全物理隔绝的设备作为冷钱包,只负责签名,签名可以通过扫描联网设备的二维码进行,同样签名后的数据可以通过二维码或者其他离线方式被联网的设备读取;
网络===onchain链上?
如果再极端一点,两台设备都离线呢?比特币的功能扩展之一:闪电网络或叫payment channel 允许两台设备做链下交易,意思是这些交易可以在某段时间脱离区块链发生在链下,最后关闭交易的时候才广播到链上;
所以说,至少两台设备可以在这期间暂时离开区块链连接,只需要这两台设备之间可以通过某种方式进行通信即可,所以理论上两台设备可以不通过互联网,不通过无线电或卫星,可以通过本地连接比如 mesh network 连接即可 所以总而言之,不管采用什么方式,最终都需要广播给区块链网络,显然就这方面的能力而言,比特币是更加强大的。
前文以一些具体的交易为例讲解了围绕基本的比特币转账交易的知识点,区块是由众多交易组成的,每个都区块(创世区块除外)通过区块头header中包含的前一个区块的哈希值hash来指向或链接到前一个区块,从而形成了区块链,每个区块的哈希值hash是根据整个区块的各种变量计算出来的,是独一无二的,所以如果想篡改某个区块,就必须篡改其后面的所有区块,根据前文的分析,6个区块确认之后,这种可能性属于小概率事件; 对于比特币来说,这个“链”除了体现在后一个区块到前一个区块的链接外,还体现在:每个交易本身的输入vin都会指向或链接到前一个交易(coinbase矿工奖励除外)
首先大家常见的一种账户余额模型就是最基本的Account/Balance模型,比如你的银行账户或者支付宝账户,你注册的名字或邮箱或手机对应某个总的数值,代表你的总存款或者总余额。
这种账户余额模型也常见于很多区块链项目中,比如熟知的以太坊就是采用这个模型,只不过跟传统的保存在中心化数据库中的做法不同,区块链是将帐户余额保存在链上的,如下图
不要被这张图吓到,我们只需要看:
最左边是区块,账户余额就是保存在 StateRoot 所指向的 Word State trie 即所谓的 世界观树,当然,随着交易的不断发生,这个世界观树也是随着区块不断刷新的,具体举例如下:
账户 14c5f88a 转账给 账户 bb75a980,可以看到转账之后的State数据中,相应的账户金额发生了变化
而比特币中是完全没有这种所谓世界观的概念的,比特币也不会维护某个账户的余额,通过前一篇文章讲解的基本交易示例,我们知道,交易发生后,会输出vout,我上次说,比特币就是长这个样子:
"vout": [
{
"value": 0.10000000,
"n": 0,
"scriptPubKey": {
"asm": "OP_HASH160 82b0b02e998beaf0fdd8382819b7d10160902eeb OP_EQUAL",
"hex": "a91482b0b02e998beaf0fdd8382819b7d10160902eeb87",
"reqSigs": 1,
"type": "scripthash",
"addresses": [
"2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi"
]
}
},
上面的地址
2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi
对应的hash160就是
82b0b02e998beaf0fdd8382819b7d10160902eeb
是不是说地址
2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi
就只有0.1个比特币呢?
非也,咱们挖一下代码吧(不懂代码的同学不用紧张,不影响理解)
# Create a chain of transactions from given utxo, and add to a new block.
def build_block_with_transactions(self, node, utxo, num_transactions):
block = self.build_block_on_tip(node)
for i in range(num_transactions):
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b''))
tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
tx.rehash()
utxo = [tx.sha256, 0, tx.vout[0].nValue]
block.vtx.append(tx)
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
return block
只需要大意理解下,可以看到构造transaction之后会构造了一个unspent transaction output - utxo,就是这么个玩意:
utxo {
txid # 交易的哈希值
vout # 输出的序列号(第几个vout输出)
value # 输出数值
}
这才算是比特币真正的长相,
由于可能有很多个utxo都是包含了
2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi
这个地址的,如果你将这个地址视为一个帐号,那么这个账号在比特币这里并没有对应某个状态数据来表示它有多少比特币,而是对应了一堆的 utxo,所以想要知道这个地址下有多少比特币,比特币钱包需要做的就是聚合所有的utxo来统计一个所谓的“余额”: ` lyhistory@lyhistory-VirtualBox:/opt/bitcoin$ ./src/bitcoin-cli listunspent` 注意,图中 Alice 可能有多个地址,如果只想知道其中某个地址的utxo,可以这么玩:
lyhistory@lyhistory-VirtualBox:/opt/bitcoin$ ./src/bitcoin-cli listunspent "[\"2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi\"]"
通过前面未花费输出 utxo的讲解,我们知道了 Alice的某个地址如 2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi
可能有多个utxo:
{txid-0001....amount: 0.1 .....}
{txid-0002....amount: 0.3 .....}
{txid-0003....amount: 1.3 .....}
如果Alice要给Bob转 0.35个比特币,有几种方式:
使用掉1.3,或者将0.1和0.3凑起来使用,无论是哪种方式,所使用的utxo需要整个消费掉,然后产生新的utxo,如果不考虑手续费,假设使用了 1.3,剩下的 1.3 - 0.35 去哪了?
通常Alice需要指定一个自己的接收地址来接收剩下钱,也就是 1.3 - 0.35 的去向,当然这个地址可以是 2N5AFVjXYG9ECxSV6Ha6quSrmRGafK1AxGi 或者其他,否则就是全部交矿工费了,一般钱包都会默认帮你做这件事情,除非你学我使用命令行,但是学艺不精的话就惨了,交给矿工当学费了!
转账完,Alice的utxo如下
{txid-0001....amount: 0.1 .....}
{txid-0002....amount: 0.3 .....}
{txid-0011....amount: 0.95 .....}
可以看到 txid-0003 整个被花掉了,产生了新的 utxo
参考资料:
区块链教程: https://lyhistory.com/docs/blockchain/
以太坊白皮书: https://ethereum.org/en/whitepaper/
闪电网络: https://www.coindesk.com/bootstrapping-mobile-mesh-networks-with-bitcoin-lightning