区块链自问自答 day4
区块链自问自答 day4
为什么使用UTXO?
UTXO完全模拟了现金的使用方式,花费然后找零,不同的是每一笔UTXO的去向和来源都是可以追根溯源的。
使用UTXO也有助于提升匿名性,通过生成大量的地址去接收和发送UTXO能够更好的保护隐私。
使用UTXO相比余额系统也更不容易被篡改,任何UTXO非法的增加和减少都会引起账本的冲突,无法达成收支平衡。
在去中心化系统中,账户的创建和销毁是无法被全部追踪到的,会大幅提升对节点的计算能力要求,这将破坏系统的去中心化程度。
为了确定一笔UTXO是否被消耗或者说确定同一笔交易是否被重复交易(重放攻击),在中心化系统中需要设计一个唯一的标识ID让服务器知道这是同一笔交易。而在去中心化系统中不存在这样一个中心服务器统一分配ID,必须要从创世区块开始往后遍历才能确定是否是重复交易。比特币中就把所有UTXO放入叫UTXO set的缓存中,很大的减少了节点的负担。
计算是在链外的,交易本身既是结果也是证明。节点只做验证即可,不需要对交易进行额外的计算,也没有额外的状态存储。交易本身的输出 UTXO 的计算是在钱包完成的,这样交易的计算负担完全由钱包来承担,一定程度上减少了链的负担。
UTXO有什么缺点?
- UTXO的无状态模型会迫使交易包含状态信息,这使合约的设计不必要地复杂化。
- 无法实现一些比较复杂的逻辑,可编程性差。
- UTXO实现的复杂性在实际使用中会比理论上还要大,这样的复杂性是没有必要的。
交易中的签名是如何构造的?
签名的过程是先将需要签名的信息做散列处理,然后用私钥对这段散列值进行加密运算,得到签名信息。发送者将原信息和签名一起递交给接收者,接收者使用发送者的公钥对签名进行解密,还原出散列值,再通过散列算法验证信息的散列值和解密签名还原出来的散列值是否一致,从而鉴定信息是否来自发送者或验证信息是否被篡改。
每个交易Tx中都包括n个input + m个output + LockTime。由于一个交易的输入、输出可能具有多个,因此签名也具有多种类型。
-
SIGHASH_ALL
此签名类型为默认类型,也是目前绝大部分交易采用的。对所有交易信息进行签名,n个输入就有n个签名,意思是我认可笔交易里的输入、输出,并且我同意花费我的那笔输入。
-
SIGHASH_NONE
该签名是仅对输入和锁定时间签名,不对输出签名。意思是我认可这笔交易里的输入,并且同意花费我的花费,至于这笔钱花到谁身上去了,我并不在乎。
-
SIGHASH_SINGLE
这种类型的签名是仅对自己的输入、输出签名,并留空其他sequence字段。意思是这笔单子中,我同意花费我的那笔钱,且只能花费到我认可的输出,至于单子中的其他输入、输出,我不关心。
-
SIGHASH_NOINPUT
这种签名模式运用于闪电网络
构造包含输入、输出和锁定时间的空白交易后,使用ECDSA算法对空白交易的"hex”字段进行签名,签名完成后将签名值填入空白字段中,从而得到完整的交易。最后将交易广播到网络中的节点。
交易中的脚本是如何运作的?
比特币的交易思路如下:交易的发起者悬赏若干比特币,并在网络上贴出了一个难题,谁有这个题的解,悬赏就归谁。顺着这个思路,Alice对Bob的转账可以理解为Alice把一道只有Bob才能解开的题发到网络上,Bob解出题并拿走了悬赏。那么,每个交易数据中出现的”脚本“就是题和解,”脚本语言“就是用来描述题和解的工具。
在交易结构中,我们看到有"vin”字段和”vout“字段,在”vin“字段中的”scriptSig“中的”asm“是一长串的字符,那是脚本的公钥和签名组成的字符串,在比特币的交易思路中,这一长串字符就是上一笔输出也就是UTXO的解。
可以看到”vout“字段中存在类似汇编语言的语句,这就是比特币交易思路中的题。在解题的过程中,会将想要尝试解题的“scriptSig”与”scriptPubKey“进行连接。形成"<Sig><PublicKey> OP_DUP OP_HASH160 <PublicKeyHash> OP_EQUALVERIFY OP_CHECKSIG”的脚本执行语句。
意思是,先将收款人的签名和公钥压入栈,此时收款人公钥在栈顶。再使用OP_DUP复制栈顶再压入栈,此时栈中三个元素从上到下PublicKey、PublicKey、Sig。再使用OP_HASH160对栈顶元素进行SHA256散列,再进行ripemd-160散列。将输出脚本中的公钥散列压入栈(就是那一串不知含义的字符串)。使用OP_EQUALVERIFY弹出栈顶两个元素,进行比较是否相等,相等则继续执行下个命令,不相等则压入false。最后进行验签操作,如果相等返回成功,否则返回失败。
交易是如何被验证的?
在新创建一笔交易后,会将交易广播到网络中的节点,每个节点接收到交易都会对交易进行验证,确保只有有效的交易才会在网络中传播。
在验证交易时节点都会遵从一个标准列表,例如:
- 交易的语法和数据结构必须正确
- 输入与输出列表都不能为空
- 交易的字节大小是小于blockmaxweight的(bitcoin core 0.16.1及以上版本)
- 每一个输出值以及总量,必须在规定值的范围内(大于0,小于2x10⁶个币)
- 没有散列等于0,N等于-1的输入(Coinbase交易不应被中继)
- nLockTime是小于或等于INT_MAX的
- 交易的字节大小是大于或等于100的
- 交易中的签名数量应小于签名操作数量的上限
- 解锁脚本只能够将数字压入栈中,并且锁定脚本必须符合isStandard的格式(该格式会拒绝非标准交易)
- 池中或位于主分支区块中的一个匹配交易必须是存在的
- 对于每一个输入,如果引用的输出存在于池中任何的交易,该交易将被拒绝
- 对于每一个输入,在主分支和交易池中寻找引用的输出交易。如果输出交易缺少任何一个输入,该交易将称为一个孤立的交易。如果与其匹配的交易还没有出现在池中,那么将被加入孤立交易池。
- 对于每一个输入,引用的输出是必须存在,并且没有被花费的
- 对于每一个输入,如果引用的输出交易是一个Coinbase输出,该输入必须至少获得COINBASE_MATURITY个确认
- 使用引用的输出交易获得输入值,并检查每一个输入值和总值是否在规定值的范围内(大于0,小于2.1x10⁶个币)
- 如果输入值的总和小于输出值的总和,交易将被终止
- 如果交易费用太低以至于无法进入空的区块,交易将被拒绝
- 每一个输入的解锁脚本必须依据相应输出的锁定脚本来验证
这些条件随时间发生变化,为了处理新型拒绝服务攻击,有时也因为交易类型多样化而放宽规则。
节点接收到交易进行验证后,会按接收的顺序建立交易池。