From 3331d6098ba6b1605bfdaaa2de1becfc77e94d74 Mon Sep 17 00:00:00 2001 From: David Hill Date: Sat, 1 Aug 2015 10:43:06 -0400 Subject: [PATCH] txscript: New function IsUnspendable IsUnspendable takes a public key script and returns whether it is spendable. Additionally, hook this into the mempool isDust function, since unspendable outputs can't be spent. This mimics Bitcoin Core commit 0aad1f13b2430165062bf9436036c1222a8724da --- mempool.go | 5 +++++ txscript/script.go | 12 ++++++++++++ txscript/script_test.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/mempool.go b/mempool.go index ebcaf5f7..360ff80f 100644 --- a/mempool.go +++ b/mempool.go @@ -107,6 +107,11 @@ type txMemPool struct { // relay fee. In particular, if the cost to the network to spend coins is more // than 1/3 of the minimum transaction relay fee, it is considered dust. func isDust(txOut *wire.TxOut) bool { + // Unspendable outputs are considered dust. + if txscript.IsUnspendable(txOut.PkScript) { + return true + } + // The total serialized size consists of the output and the associated // input script to redeem it. Since there is no input script // to redeem it yet, use the minimum size of a typical input script. diff --git a/txscript/script.go b/txscript/script.go index a811680a..a9c74939 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -458,3 +458,15 @@ func GetPreciseSigOpCount(scriptSig, scriptPubKey []byte, bip16 bool) int { shPops, _ := parseScript(shScript) return getSigOpCount(shPops, true) } + +// IsUnspendable returns whether the passed public key script is unspendable, or +// guaranteed to fail at execution. This allows inputs to be pruned instantly +// when entering the UTXO set. +func IsUnspendable(pkScript []byte) bool { + pops, err := parseScript(pkScript) + if err != nil { + return true + } + + return len(pops) > 0 && pops[0].opcode.value == OP_RETURN +} diff --git a/txscript/script_test.go b/txscript/script_test.go index 21b88af7..012d07f8 100644 --- a/txscript/script_test.go +++ b/txscript/script_test.go @@ -486,3 +486,38 @@ func TestIsPushOnlyScript(t *testing.T) { "%v", test.name, true, test.expected) } } + +// TestIsUnspendable ensures the IsUnspendable function returns the expected +// results. +func TestIsUnspendable(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + pkScript []byte + expected bool + }{ + { + // Unspendable + pkScript: []byte{0x6a, 0x04, 0x74, 0x65, 0x73, 0x74}, + expected: true, + }, + { + // Spendable + pkScript: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0, + 0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45, + 0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6, + 0xfa, 0x0b, 0x5c, 0x88, 0xac}, + expected: false, + }, + } + + for i, test := range tests { + res := txscript.IsUnspendable(test.pkScript) + if res != test.expected { + t.Errorf("TestIsUnspendable #%d failed: got %v want %v", + i, res, test.expected) + continue + } + } +}