Skip to content

Conversation

sipa
Copy link
Member

@sipa sipa commented May 9, 2018

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.

OP_PUBKEYS = 0xfb,
OP_PUBKEYHASH = 0xfd,
OP_PUBKEY = 0xfe,

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to get rid of these fake opcodes here.

Copy link
Contributor

@jtimon jtimon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

CScript::const_iterator it = script.begin();
if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;

if (!script.GetOp(it, opcode, data) || (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16))) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Souldn't this return false if it's op_0 too? or are 0 required sigs multisigs valid?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key count can be 0, I think. The only checks in the interpreter I see are for negative numbers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to stick to the original logic as close as possible; optimizations / other tweaks can happen later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with @sipa here. One step at a time is easier to review.

}
if (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, shouldn't OP_0 be invalid here too?
If 0 is allowed, why not just opcode < OP_0 || opcode > OP_16?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OP_0 is 0, OP_1 is 81. They aren't continuous.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An isOP_N(opcode) helper function would make this (and the expression above) slightly better readable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an IsSmallInteger function for this (which would be distinct from IsOP_N by not accepting OP_1NEGATE).

mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG) {
pubkey = valtype(script.begin() + 1, script.begin() + 34);
return CPubKey::ValidSize(pubkey);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant check here? We know the size.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CpubKey::ValidSize checks the data-dependent size (depending on what the first byte is).

}
if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG) {
pubkey = valtype(script.begin() + 1, script.begin() + 66);
return CPubKey::ValidSize(pubkey);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant check here? We know the size.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just use proper constants in the lines above for more belt and suspenders.

@sipa
Copy link
Member Author

sipa commented May 9, 2018

@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.

@instagibbs
Copy link
Member

light tACK


if (!script.GetOp(it, opcode, data) || (opcode != OP_0 && (opcode < OP_1 || opcode > OP_16))) return false;
required = CScript::DecodeOP_N(opcode);
while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed (that's also what the old code does).

@sipa sipa force-pushed the 201805_noscriptpattern branch from 688d796 to 2fc4fdd Compare May 12, 2018 20:31
pubkey = valtype(script.begin() + 1, script.begin() + 34);
return CPubKey::ValidSize(pubkey);
}
if (script.size() == 67 && script[0] == 65 && script[66] == OP_CHECKSIG) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPubKey::PUBLIC_KEY_SIZE == 65, would add clarity

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{
// Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
if (script.size() == 35 && script[0] == 33 && script[34] == OP_CHECKSIG) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPubKey::COMPRESSED_PUBLIC_KEY_SIZE == 33, would add clarity

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally, I have #12461 to make these references more succinct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return false;
}

static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not merging with the versions in compressor - they may end up living separate lives as the standard templates change.

@@ -35,22 +35,55 @@ const char* GetTxnOutputType(txnouttype t)
return nullptr;
}

bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also IsToPubKey in compressor, which looks equivalent.

@Empact
Copy link
Contributor

Empact commented May 16, 2018

Concept ACK! Much simpler way to handle these 3 cases.

@jonasschnelli
Copy link
Contributor

Nice!
utACK 2fc4fdd0945a3800af8213676182ebb2c63689d8

@sipa sipa force-pushed the 201805_noscriptpattern branch from 2fc4fdd to fa1f642 Compare May 17, 2018 00:14
@gmaxwell
Copy link
Contributor

ACK.

}
if (!IsSmallInteger(opcode)) return false;
unsigned int keys = CScript::DecodeOP_N(opcode);
if (pubkeys.size() != keys || required == 0 || keys < required) return false;
Copy link
Contributor

@Empact Empact May 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this required == 0 check could be an earlier return

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

typeRet = TX_MULTISIG;
vSolutionsRet.push_back({static_cast<unsigned char>(required)});
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth noting these casts are safe due to small integer limits?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@Empact
Copy link
Contributor

Empact commented May 26, 2018

utACK fa1f642

@sipa sipa force-pushed the 201805_noscriptpattern branch from fa1f642 to 89736a9 Compare May 26, 2018 00:39
@Empact
Copy link
Contributor

Empact commented May 26, 2018

re-utACK 89736a9 just addresses comments above

if (!IsSmallInteger(opcode)) return false;
unsigned int keys = CScript::DecodeOP_N(opcode);
if (pubkeys.size() != keys || keys < required) return false;
if (it + 1 != script.end()) return false;
Copy link
Contributor

@promag promag May 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@promag
Copy link
Contributor

promag commented May 26, 2018

utACK 89736a9.

@sipa sipa force-pushed the 201805_noscriptpattern branch from 89736a9 to c639f5a Compare May 26, 2018 18:24
{
// Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script[CPubKey::PUBLIC_KEY_SIZE + 1] == OP_CHECKSIG) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about script.back() == OP_CHECKSIG?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/** Test for "small integer" script opcodes - OP_0 through OP_16. */
static constexpr bool IsSmallInteger(opcodetype opcode)
{
return opcode == OP_0 || (opcode >= OP_1 && opcode <= OP_16);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you remove opcode == OP_0 here, you can remove if (required == 0) return false; below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

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.
@sipa sipa force-pushed the 201805_noscriptpattern branch from c639f5a to c814e2e Compare May 29, 2018 21:42
@jonasschnelli
Copy link
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)

sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Oct 10, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Oct 17, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Oct 17, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Oct 17, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Oct 17, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Oct 30, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Dec 6, 2019
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
zkbot added a commit to zcash/zcash that referenced this pull request Dec 17, 2019
sickpig pushed a commit to sickpig/BitcoinUnlimited that referenced this pull request Mar 11, 2020
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.

Committer's note:

This is a port of bitcoin/bitcoin/pull/13194 tha has been adapted to
work with BU. The main differencies are related to the fact that we
expose to the user two other kind of transaction use cases: TX_LABELPUBLIC, TX_CLTV.
furszy added a commit to PIVX-Project/PIVX that referenced this pull request Jun 27, 2020
5d96c91 add missing policy.cpp file to cmake (furszy)
2d0783d Standard Solver(): Include cold staking template matching. (furszy)
b7fe4c0 Remove template matching and pseudo opcodes (furszy)
fe31b31 Assert CPubKey::ValidLength to the pubkey's header-relevent size (Ben Woosley)
2f6b249 Make TX_SCRIPTHASH clear vSolutionsRet first (Peter Todd)
45e5102 Policy: MOVEONLY: 3 functions to policy (furszy)
2c7da7f Policy: MOVEONLY: Create policy/policy.h with some constants (furszy)

Pull request description:

  Coming from the following PRs (with small customizations for our features):

  * bitcoin#6335
  * bitcoin#6424
  * bitcoin#12460
  * bitcoin#13194

ACKs for top commit:
  random-zebra:
    Looking good. ACK 5d96c91
  Fuzzbawls:
    ACK 5d96c91

Tree-SHA512: b3e7479864e16942682b9bc19f432b2a657561647eeadf0ef1285651feb05875d295ff530582ebbc8edacaf3c7d3888249513b4a93d871c2d6c2e1b157ace8c8
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 19, 2021
c814e2e Remove template matching and pseudo opcodes (Pieter Wuille)

Pull request description:

  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.

Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 24, 2021
c814e2e Remove template matching and pseudo opcodes (Pieter Wuille)

Pull request description:

  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.

Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 26, 2021
c814e2e Remove template matching and pseudo opcodes (Pieter Wuille)

Pull request description:

  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.

Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 26, 2021
c814e2e Remove template matching and pseudo opcodes (Pieter Wuille)

Pull request description:

  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.

Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 28, 2021
c814e2e Remove template matching and pseudo opcodes (Pieter Wuille)

Pull request description:

  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.

Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Dec 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants