Published: 2018-12-10

BIP30(duplicate transactions)

1 BIP地址

BIP30

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
                }
        }

        /* 省略其它 */
}

5 END

Author: Nisen

Email: imnisen@163.com