BIP30(duplicate transactions)
1 BIP地址
BIP: 30 Layer: Consensus (soft fork) Title: Duplicate transactions Author: Pieter Wuille <pieter.wuille@gmail.com> Comments-Summary: No comments yet. Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-0030 Status: Final Type: Standards Track Created: 2012-02-22 License: BSD-2-Clause
2 原因和目的
Bitcoin参考实现默认重复交易(txid相同)不存在,但这是不对的,特别是coinbase tx很容易去重复。针对重复交易可以发起一些攻击来时的完全确认但交易回滚,从而导致双花等问题。
BIP30提出了一个处理重复交易的方案。
3 细节
规则是: 不允许包含与未完全花费的交易id相同id的交易到区块链。
Mainnet从March 15, 2012, 00:00 UTC (testnet: February 20, 2012 00:00 UTC)开始应用,除去两个历史的例外block(blocks at heights 91842 and 91880)
4 相关实现
参考btcd的实现
检查是不是例外的block
// isBIP0030Node returns whether or not the passed node represents one of the // two blocks that violate the BIP0030 rule which prevents transactions from // overwriting old ones. func isBIP0030Node(node *blockNode) bool { if node.height == 91842 && node.hash.IsEqual(block91842Hash) { return true } if node.height == 91880 && node.hash.IsEqual(block91880Hash) { return true } return false
检查是否和未花费的utxo相同id
// checkBIP0030 ensures blocks do not contain duplicate transactions which // 'overwrite' older transactions that are not fully spent. This prevents an // attack where a coinbase and all of its dependent transactions could be // duplicated to effectively revert the overwritten transactions to a single // confirmation thereby making them vulnerable to a double spend. // // For more details, see // https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki and // http://r6.ca/blog/20120206T005236Z.html. // // This function MUST be called with the chain state lock held (for reads). func (b *BlockChain) checkBIP0030(node *blockNode, block *btcutil.Block, view *UtxoViewpoint) error { // Fetch utxos for all of the transaction ouputs in this block. // Typically, there will not be any utxos for any of the outputs. fetchSet := make(map[wire.OutPoint]struct{}) for _, tx := range block.Transactions() { prevOut := wire.OutPoint{Hash: *tx.Hash()} for txOutIdx := range tx.MsgTx().TxOut { prevOut.Index = uint32(txOutIdx) fetchSet[prevOut] = struct{}{} } } err := view.fetchUtxos(b.db, fetchSet) if err != nil { return err } // Duplicate transactions are only allowed if the previous transaction // is fully spent. for outpoint := range fetchSet { utxo := view.LookupEntry(outpoint) if utxo != nil && !utxo.IsSpent() { str := fmt.Sprintf("tried to overwrite transaction %v "+ "at block height %d that is not fully spent", outpoint.Hash, utxo.BlockHeight()) return ruleError(ErrOverwriteTx, str) } } return nil }
检查区块有效性的方法,核心是调用上面两个方法
func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos *[]SpentTxOut) error { /* 省略其它 */ // BIP0030 added a rule to prevent blocks which contain duplicate // transactions that 'overwrite' older transactions which are not fully // spent. See the documentation for checkBIP0030 for more details. // // There are two blocks in the chain which violate this rule, so the // check must be skipped for those blocks. The isBIP0030Node function // is used to determine if this block is one of the two blocks that must // be skipped. // // In addition, as of BIP0034, duplicate coinbases are no longer // possible due to its requirement for including the block height in the // coinbase and thus it is no longer possible to create transactions // that 'overwrite' older ones. Therefore, only enforce the rule if // BIP0034 is not yet active. This is a useful optimization because the // BIP0030 check is expensive since it involves a ton of cache misses in // the utxoset. if !isBIP0030Node(node) && (node.height < b.chainParams.BIP0034Height) { err := b.checkBIP0030(node, block, view) if err != nil { return err } } /* 省略其它 */ }