Remove template matching and pseudo opcodes #13194

pull sipa wants to merge 1 commits into bitcoin:master from sipa:201805_noscriptpattern changing 4 files +66 −103
  1. sipa commented at 3:27 am on May 9, 2018: member

    The current code contains a rather complex script template matching engine, which is only used for 3 particular script types (P2PK, P2PKH, multisig). The first two of these are trivial to match for otherwise, and a specialized matcher for multisig is both more compact and more efficient than a generic one.

    The goal is being more flexible, so that for example larger standard multisigs inside SegWit outputs are easier to implement.

    As a side-effect, it also gets rid of the pseudo opcodes hack.

  2. laanwj added the label Wallet on May 9, 2018
  3. in src/script/script.h:190 in 688d79698f outdated
    185-    // template matching params
    186-    OP_SMALLINTEGER = 0xfa,
    187-    OP_PUBKEYS = 0xfb,
    188-    OP_PUBKEYHASH = 0xfd,
    189-    OP_PUBKEY = 0xfe,
    190-
    


    laanwj commented at 11:18 am on May 9, 2018:
    Nice to get rid of these fake opcodes here.
  4. in src/script/standard.cpp:67 in 688d79698f outdated
    73+    opcodetype opcode;
    74+    valtype data;
    75+    CScript::const_iterator it = script.begin();
    76+    if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
    77+
    78+    if (!script.GetOp(it, opcode, data) || (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16))) return false;
    


    jtimon commented at 2:22 pm on May 9, 2018:
    Souldn’t this return false if it’s op_0 too? or are 0 required sigs multisigs valid?

    instagibbs commented at 3:17 pm on May 9, 2018:
    key count can be 0, I think. The only checks in the interpreter I see are for negative numbers.

    sipa commented at 3:36 pm on May 9, 2018:
    I’m trying to stick to the original logic as close as possible; optimizations / other tweaks can happen later.

    laanwj commented at 11:25 am on May 10, 2018:
    Agree with @sipa here. One step at a time is easier to review.
  5. in src/script/standard.cpp:72 in 688d79698f outdated
    78+    if (!script.GetOp(it, opcode, data) || (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16))) return false;
    79+    required = CScript::DecodeOP_N(opcode);
    80+    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
    81+        pubkeys.emplace_back(std::move(data));
    82     }
    83+    if (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)) return false;
    


    jtimon commented at 2:26 pm on May 9, 2018:
    Same here, shouldn’t OP_0 be invalid here too? If 0 is allowed, why not just opcode < OP_0 || opcode > OP_16?

    sipa commented at 3:31 pm on May 9, 2018:
    OP_0 is 0, OP_1 is 81. They aren’t continuous.

    laanwj commented at 12:48 pm on May 10, 2018:
    An isOP_N(opcode) helper function would make this (and the expression above) slightly better readable.

    sipa commented at 8:32 pm on May 12, 2018:
    I added an IsSmallInteger function for this (which would be distinct from IsOP_N by not accepting OP_1NEGATE).
  6. jtimon approved
  7. jtimon commented at 2:54 pm on May 9, 2018: contributor
    utACK modulo nits and modulo this kind of unrelated thing I don’t understand. Perhaps a stupid question but, in https://github.com/bitcoin/bitcoin/blob/master/src/pubkey.h#L59 why are there 3 ways to indicate non-compressed and 2 ways to indicate compressed? shouldn’t one for each be enough? also, should using an enum or macros or constants be more clear?
  8. in src/script/standard.cpp:42 in 688d79698f outdated
    44-    {
    45-        // Standard tx, sender provides pubkey, receiver adds signature
    46-        mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
    47+    if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG) {
    48+        pubkey = valtype(script.begin() + 1, script.begin() + 34);
    49+        return CPubKey::ValidSize(pubkey);
    


    instagibbs commented at 3:18 pm on May 9, 2018:
    Redundant check here? We know the size.

    sipa commented at 3:32 pm on May 9, 2018:
    CpubKey::ValidSize checks the data-dependent size (depending on what the first byte is).
  9. in src/script/standard.cpp:46 in 688d79698f outdated
    48+        pubkey = valtype(script.begin() + 1, script.begin() + 34);
    49+        return CPubKey::ValidSize(pubkey);
    50+    }
    51+    if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG) {
    52+        pubkey = valtype(script.begin() + 1, script.begin() + 66);
    53+        return CPubKey::ValidSize(pubkey);
    


    instagibbs commented at 3:18 pm on May 9, 2018:
    Redundant check here? We know the size.

    instagibbs commented at 3:19 pm on May 9, 2018:
    We could just use proper constants in the lines above for more belt and suspenders.
  10. sipa commented at 3:33 pm on May 9, 2018: member
    @jtimon Hybrid public keys (the 0x06 and 0x07 ones) are stupid, but were supported by OpenSSL, and because of that they’re supported in Bitcoin. Nothing we can do about that.
  11. instagibbs commented at 3:46 pm on May 9, 2018: member
    light tACK
  12. in src/script/standard.cpp:75 in 688d79698f outdated
    75+    CScript::const_iterator it = script.begin();
    76+    if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
    77+
    78+    if (!script.GetOp(it, opcode, data) || (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16))) return false;
    79+    required = CScript::DecodeOP_N(opcode);
    80+    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
    


    laanwj commented at 12:42 pm on May 10, 2018:
    Shouldn’t it check the opcodes here? Oh I guess not, all the push opcodes valid here will return non-empty data, and CPubKey::ValidSize rejects empty data.

    sipa commented at 8:31 pm on May 12, 2018:
    Indeed (that’s also what the old code does).
  13. sipa force-pushed on May 12, 2018
  14. in src/script/standard.cpp:44 in 2fc4fdd094 outdated
    46-        mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
    47+    if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG) {
    48+        pubkey = valtype(script.begin() + 1, script.begin() + 34);
    49+        return CPubKey::ValidSize(pubkey);
    50+    }
    51+    if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG) {
    


    Empact commented at 2:06 am on May 16, 2018:
    CPubKey::PUBLIC_KEY_SIZE == 65, would add clarity

    sipa commented at 0:15 am on May 17, 2018:
    Done.
  15. in src/script/standard.cpp:40 in 2fc4fdd094 outdated
    42-    static std::multimap<txnouttype, CScript> mTemplates;
    43-    if (mTemplates.empty())
    44-    {
    45-        // Standard tx, sender provides pubkey, receiver adds signature
    46-        mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
    47+    if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG) {
    


    Empact commented at 2:06 am on May 16, 2018:
    CPubKey::COMPRESSED_PUBLIC_KEY_SIZE == 33, would add clarity

    Empact commented at 2:10 am on May 16, 2018:
    Incidentally, I have #12461 to make these references more succinct.

    sipa commented at 0:15 am on May 17, 2018:
    Done.
  16. in src/script/standard.cpp:51 in 2fc4fdd094 outdated
    53+        return CPubKey::ValidSize(pubkey);
    54+    }
    55+    return false;
    56+}
    57+
    58+static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
    


    Empact commented at 2:36 am on May 16, 2018:

    See also IsToKeyID in compressor.cpp, which looks equivalent.

    Could split this up as CScript::IsPayToPubkeyHash, and do the unpacking in Solver, ala IsPayToScriptHash. Would make the case handling more consistent in Solver.


    sipa commented at 0:21 am on May 17, 2018:
    I’d rather not merging with the versions in compressor - they may end up living separate lives as the standard templates change.
  17. in src/script/standard.cpp:38 in 2fc4fdd094 outdated
    34@@ -35,22 +35,55 @@ const char* GetTxnOutputType(txnouttype t)
    35     return nullptr;
    36 }
    37 
    38-bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
    39+static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
    


    Empact commented at 2:37 am on May 16, 2018:
    See also IsToPubKey in compressor, which looks equivalent.
  18. Empact commented at 2:42 am on May 16, 2018: member
    Concept ACK! Much simpler way to handle these 3 cases.
  19. jonasschnelli commented at 6:20 am on May 16, 2018: contributor
    Nice! utACK 2fc4fdd0945a3800af8213676182ebb2c63689d8
  20. sipa force-pushed on May 17, 2018
  21. gmaxwell commented at 9:35 pm on May 25, 2018: contributor
    ACK.
  22. in src/script/standard.cpp:80 in fa1f642772 outdated
    86+    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
    87+        pubkeys.emplace_back(std::move(data));
    88     }
    89+    if (!IsSmallInteger(opcode)) return false;
    90+    unsigned int keys = CScript::DecodeOP_N(opcode);
    91+    if (pubkeys.size() != keys || required == 0 || keys < required) return false;
    


    Empact commented at 0:29 am on May 26, 2018:
    nit: this required == 0 check could be an earlier return

    sipa commented at 0:39 am on May 26, 2018:
    Fixed.
  23. in src/script/standard.cpp:150 in fa1f642772 outdated
    221+    std::vector<std::vector<unsigned char>> keys;
    222+    if (MatchMultisig(scriptPubKey, required, keys)) {
    223+        typeRet = TX_MULTISIG;
    224+        vSolutionsRet.push_back({static_cast<unsigned char>(required)});
    225+        vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
    226+        vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())});
    


    Empact commented at 0:30 am on May 26, 2018:
    Maybe worth noting these casts are safe due to small integer limits?

    sipa commented at 0:39 am on May 26, 2018:
    Done.
  24. Empact commented at 0:31 am on May 26, 2018: member
    utACK fa1f642
  25. sipa force-pushed on May 26, 2018
  26. Empact commented at 0:43 am on May 26, 2018: member
    re-utACK 89736a9 just addresses comments above
  27. in src/script/standard.cpp:82 in 89736a9fbe outdated
    88+        pubkeys.emplace_back(std::move(data));
    89     }
    90+    if (!IsSmallInteger(opcode)) return false;
    91+    unsigned int keys = CScript::DecodeOP_N(opcode);
    92+    if (pubkeys.size() != keys || keys < required) return false;
    93+    if (it + 1 != script.end()) return false;
    


    promag commented at 8:46 am on May 26, 2018:

    nit

    0return it + 1 == script.end();
    

    sipa commented at 6:24 pm on May 26, 2018:
    Done.
  28. promag commented at 8:47 am on May 26, 2018: member
    utACK 89736a9.
  29. sipa force-pushed on May 26, 2018
  30. in src/script/standard.cpp:40 in c639f5ac20 outdated
    42-    static std::multimap<txnouttype, CScript> mTemplates;
    43-    if (mTemplates.empty())
    44-    {
    45-        // Standard tx, sender provides pubkey, receiver adds signature
    46-        mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
    47+    if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script[CPubKey::PUBLIC_KEY_SIZE + 1] == OP_CHECKSIG) {
    


    jl2012 commented at 4:53 pm on May 29, 2018:
    what about script.back() == OP_CHECKSIG?

    sipa commented at 9:42 pm on May 29, 2018:
    Done.
  31. Remove template matching and pseudo opcodes
    The current code contains a rather complex script template matching engine,
    which is only used for 3 particular script types (P2PK, P2PKH, multisig).
    The first two of these are trivial to match for otherwise, and a specialized
    matcher for multisig is both more compact and more efficient than a generic
    one.
    
    The goal is being more flexible, so that for example larger standard multisigs
    inside SegWit outputs are more easy to implement.
    
    As a side-effect, it also gets rid of the pseudo opcodes hack.
    c814e2e7e8
  32. in src/script/standard.cpp:63 in c639f5ac20 outdated
    67+}
    68+
    69+/** Test for "small integer" script opcodes - OP_0 through OP_16. */
    70+static constexpr bool IsSmallInteger(opcodetype opcode)
    71+{
    72+    return opcode == OP_0 || (opcode >= OP_1 && opcode <= OP_16);
    


    jl2012 commented at 5:02 pm on May 29, 2018:
    if you remove opcode == OP_0 here, you can remove if (required == 0) return false; below

    sipa commented at 9:42 pm on May 29, 2018:
    Fixed.
  33. sipa force-pushed on May 29, 2018
  34. jl2012 commented at 9:07 am on May 30, 2018: contributor
    utACK c814e2e
  35. laanwj commented at 2:50 pm on May 30, 2018: member
    utACK c814e2e7e81fd01fcb07f4a28435741bdc463801
  36. laanwj merged this on May 30, 2018
  37. laanwj closed this on May 30, 2018

  38. laanwj referenced this in commit fd96d54f39 on May 30, 2018
  39. jonasschnelli commented at 9:09 am on August 30, 2018: contributor
    This change has the side effect that invalid pubkeys (with correct size of 33 or 65) in bare multisig are now non-standard (see #14104)
  40. jonasschnelli added the label Needs release notes on Aug 30, 2018
  41. fanquake removed the label Needs release note on Mar 20, 2019
  42. sickpig referenced this in commit c2912c15e2 on Oct 10, 2019
  43. sickpig referenced this in commit 028f48415e on Oct 17, 2019
  44. sickpig referenced this in commit c62ad82032 on Oct 17, 2019
  45. sickpig referenced this in commit 79cbe72a3a on Oct 17, 2019
  46. sickpig referenced this in commit 3146c738e0 on Oct 17, 2019
  47. sickpig referenced this in commit 60c818236f on Oct 30, 2019
  48. sickpig referenced this in commit b3e587dce2 on Dec 6, 2019
  49. zkbot referenced this in commit 63c3d1ec94 on Dec 17, 2019
  50. sickpig referenced this in commit 79c1ff95be on Mar 11, 2020
  51. DeckerSU referenced this in commit 337442edc1 on Mar 17, 2020
  52. furszy referenced this in commit a5265a4db4 on Jun 27, 2020
  53. UdjinM6 referenced this in commit 6900d720b9 on Jun 19, 2021
  54. UdjinM6 referenced this in commit 23e5ca0964 on Jun 24, 2021
  55. UdjinM6 referenced this in commit b6364db126 on Jun 26, 2021
  56. UdjinM6 referenced this in commit e883e76989 on Jun 26, 2021
  57. UdjinM6 referenced this in commit e8da18d43b on Jun 28, 2021
  58. DrahtBot locked this on Dec 16, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-18 12:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me