Skip to content

Commit d4a8627

Browse files
authored
Merge pull request #4 from lnhance/mutinynet-cat-ln-hance
Add: PAIRCOMMIT
2 parents 5706e1f + 25720d9 commit d4a8627

File tree

9 files changed

+262
-12
lines changed

9 files changed

+262
-12
lines changed

Diff for: src/consensus/params.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ enum DeploymentPos : uint16_t {
3434
DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342)
3535
DEPLOYMENT_COVTOOLS, // Deployment of CHECKTEMPLATEVERIFY, ANYPREVOUT, OP_VAULT (BIP xxx)
3636
DEPLOYMENT_OP_CAT,
37-
DEPLOYMENT_LN_HANCE,
37+
DEPLOYMENT_LNHANCE,
3838
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
3939
MAX_VERSION_BITS_DEPLOYMENTS
4040
};

Diff for: src/kernel/chainparams.cpp

+8-8
Original file line numberDiff line numberDiff line change
@@ -385,10 +385,10 @@ class SigNetParams : public CChainParams {
385385
consensus.vDeployments[Consensus::DEPLOYMENT_OP_CAT].min_activation_height = 0;
386386

387387
// Deployment of OP_CAT (BIP xxx)
388-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].bit = 5;
389-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
390-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
391-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].min_activation_height = 0;
388+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].bit = 5;
389+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
390+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
391+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].min_activation_height = 0;
392392

393393
// message start is defined as the first 4 bytes of the sha256d of the block script
394394
HashWriter h{};
@@ -479,10 +479,10 @@ class CRegTestParams : public CChainParams
479479
consensus.vDeployments[Consensus::DEPLOYMENT_OP_CAT].min_activation_height = 0;
480480

481481
// Deployment of OP_CAT (BIP xxx)
482-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].bit = 5;
483-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
484-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
485-
consensus.vDeployments[Consensus::DEPLOYMENT_LN_HANCE].min_activation_height = 0;
482+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].bit = 5;
483+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
484+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
485+
consensus.vDeployments[Consensus::DEPLOYMENT_LNHANCE].min_activation_height = 0;
486486

487487
consensus.nMinimumChainWork = uint256{};
488488
consensus.defaultAssumeValid = uint256{};

Diff for: src/rpc/blockchain.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1319,7 +1319,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager&
13191319
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY);
13201320
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT);
13211321
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_COVTOOLS);
1322-
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_LN_HANCE);
1322+
SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_LNHANCE);
13231323
return softforks;
13241324
}
13251325
} // anon namespace

Diff for: src/script/interpreter.cpp

+31
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,25 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
15001500
break;
15011501
}
15021502

1503+
case OP_PAIRCOMMIT: {
1504+
// OP_PAIRCOMMIT is only available in Tapscript
1505+
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
1506+
// DISCOURAGE for OP_PAIRCOMMIT is handled in OP_SUCCESS handling
1507+
1508+
// x1 x2 -- hash
1509+
if (stack.size() < 2) {
1510+
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
1511+
}
1512+
const valtype& vch1 = stacktop(-2);
1513+
const valtype& vch2 = stacktop(-1);
1514+
1515+
uint256 hash = PairCommitHash(vch1, vch2);
1516+
1517+
popstack(stack);
1518+
popstack(stack);
1519+
stack.emplace_back(hash.begin(), hash.end());
1520+
break;
1521+
}
15031522
default:
15041523
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
15051524
}
@@ -1822,6 +1841,12 @@ template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTr
18221841
const HashWriter HASHER_TAPSIGHASH{TaggedHash("TapSighash")};
18231842
const HashWriter HASHER_TAPLEAF{TaggedHash("TapLeaf")};
18241843
const HashWriter HASHER_TAPBRANCH{TaggedHash("TapBranch")};
1844+
const HashWriter HASHER_PAIRCOMMIT{TaggedHash("PairCommit")};
1845+
1846+
uint256 PairCommitHash(const std::vector<unsigned char>& x1, const std::vector<unsigned char>& x2)
1847+
{
1848+
return (HashWriter{HASHER_PAIRCOMMIT} << x1 << x2).GetSHA256();
1849+
}
18251850

18261851
static bool HandleMissingData(MissingDataBehavior mdb)
18271852
{
@@ -2401,6 +2426,12 @@ static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CS
24012426
if (flags & SCRIPT_VERIFY_CHECKSIGFROMSTACK) continue;
24022427
return set_success(serror);
24032428
}
2429+
if (opcode == OP_PAIRCOMMIT) {
2430+
if (flags & SCRIPT_VERIFY_DISCOURAGE_PAIRCOMMIT)
2431+
return set_error(serror, SCRIPT_ERR_DISCOURAGE_OP_SUCCESS);
2432+
if (flags & SCRIPT_VERIFY_PAIRCOMMIT) continue;
2433+
return set_success(serror);
2434+
}
24042435
// New opcodes will be listed here. May use a different sigversion to modify existing opcodes.
24052436
if (is_vault_active && (opcode == OP_VAULT || opcode == OP_VAULT_RECOVER)) {
24062437
continue;

Diff for: src/script/interpreter.h

+9
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ enum : uint32_t {
188188
// Making OP_CHECKSIGFROMSTACK non-standard
189189
SCRIPT_VERIFY_DISCOURAGE_CHECKSIGFROMSTACK = (1U << 30),
190190

191+
// Validating OP_PAIRCOMMIT
192+
SCRIPT_VERIFY_PAIRCOMMIT = (1U << 31),
193+
194+
// Making OP_PAIRCOMMIT non-standard
195+
SCRIPT_VERIFY_DISCOURAGE_PAIRCOMMIT = (1U << 30),
196+
191197
// Constants to point to the highest flag in use. Add new flags above this line.
192198
//
193199
SCRIPT_VERIFY_END_MARKER
@@ -250,6 +256,9 @@ template<typename TxType>
250256
uint256 GetDefaultCheckTemplateVerifyHash(const TxType& tx, const uint256& outputs_hash, const uint256& sequences_hash,
251257
const uint32_t input_index);
252258

259+
/* PairCommit Declarations */
260+
uint256 PairCommitHash(const std::vector<unsigned char>& x1, const std::vector<unsigned char>& x2);
261+
253262
enum class SigVersion
254263
{
255264
BASE = 0, //!< Bare scripts and BIP16 P2SH-wrapped redeemscripts

Diff for: src/script/script.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ std::string GetOpName(opcodetype opcode)
149149
// Tapscript expansion
150150
case OP_INTERNALKEY : return "OP_INTERNALKEY";
151151
case OP_CHECKSIGFROMSTACK : return "OP_CHECKSIGFROMSTACK";
152+
case OP_PAIRCOMMIT : return "OP_PAIRCOMMIT";
152153

153154
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
154155

Diff for: src/script/script.h

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ enum opcodetype
217217
// Tapscript expansion
218218
OP_INTERNALKEY = 0xcb,
219219
OP_CHECKSIGFROMSTACK = 0xcc,
220+
OP_PAIRCOMMIT = 0xcd,
220221

221222
OP_INVALIDOPCODE = 0xff,
222223

Diff for: src/test/pchash_tests.cpp

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright (c) 2013-2021 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <core_io.h>
6+
#include <consensus/tx_check.h>
7+
#include <consensus/validation.h>
8+
#include <hash.h>
9+
#include <script/script.h>
10+
#include <script/solver.h>
11+
#include <script/interpreter.h>
12+
#include <script/signingprovider.h>
13+
#include <serialize.h>
14+
#include <addresstype.h>
15+
#include <validation.h>
16+
#include <streams.h>
17+
#include <test/util/setup_common.h>
18+
#include <util/strencodings.h>
19+
#include <common/system.h>
20+
#include <random.h>
21+
22+
#include <boost/test/unit_test.hpp>
23+
24+
#include <univalue.h>
25+
26+
namespace {
27+
typedef std::vector<unsigned char> valtype;
28+
typedef Span<const unsigned char> Raw;
29+
30+
static const unsigned char test1_expected_result[32] = {
31+
0x7c, 0xf7, 0x81, 0x30, 0xd1, 0x3d, 0x08, 0xb2,
32+
0xc6, 0xc6, 0xb2, 0xd9, 0x2e, 0xf1, 0xf2, 0xdd,
33+
0x72, 0x1a, 0xd7, 0x09, 0xaa, 0x81, 0x37, 0x12,
34+
0x53, 0xa6, 0xf1, 0xb6, 0x44, 0x96, 0x6f, 0x26
35+
};
36+
37+
static const unsigned char test2_expected_result[32] = {
38+
0x0f, 0xbe, 0x7f, 0xb7, 0xc3, 0xad, 0x59, 0x2c,
39+
0x5e, 0x87, 0x95, 0x17, 0x75, 0x7f, 0xfc, 0x6a,
40+
0x1e, 0xab, 0x8a, 0x94, 0xeb, 0x87, 0x94, 0xcd,
41+
0x82, 0xeb, 0x0d, 0xfc, 0x74, 0xe4, 0xbf, 0xec
42+
};
43+
}
44+
45+
BOOST_FIXTURE_TEST_SUITE(pchash_tests, BasicTestingSetup)
46+
47+
// Goal: check that PC Hash Function generate correct hash
48+
BOOST_AUTO_TEST_CASE(pchash_from_data)
49+
{
50+
uint256 hash1 = PairCommitHash(
51+
// "Hello "
52+
valtype{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20},
53+
// "World!"
54+
valtype{0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21}
55+
);
56+
BOOST_CHECK_EQUAL(hash1, uint256(test1_expected_result));
57+
58+
uint256 hash2 = PairCommitHash(
59+
// "Hello"
60+
valtype{0x48, 0x65, 0x6c, 0x6c, 0x6f},
61+
// " World!"
62+
valtype{0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21}
63+
);
64+
BOOST_CHECK_EQUAL(hash2, uint256(test2_expected_result));
65+
}
66+
67+
// Goal: check that PC Hash Function generate correct hash
68+
BOOST_AUTO_TEST_CASE(pchash_reproduce)
69+
{
70+
// pc_tag_hash = SHA256("PairCommit")
71+
uint256 pc_tag_hash;
72+
std::string pc_tag = "PairCommit";
73+
CSHA256().Write((const unsigned char*)pc_tag.data(), pc_tag.size()).Finalize(pc_tag_hash.begin());
74+
75+
// "Hello "
76+
const valtype x1 = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20};
77+
// "World!"
78+
const valtype x2 = {0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21};
79+
// CompactSize(6)
80+
const valtype x1_size = {0x06};
81+
// CompactSize(6)
82+
const valtype x2_size = {0x06};
83+
84+
uint256 hash1 = PairCommitHash(x1, x2);
85+
86+
HashWriter ss;
87+
ss << Raw{pc_tag_hash}
88+
<< Raw{pc_tag_hash}
89+
<< Raw{x1_size}
90+
<< Raw{x1}
91+
<< Raw{x2_size}
92+
<< Raw{x2};
93+
94+
uint256 hash2 = ss.GetSHA256();
95+
96+
BOOST_CHECK_EQUAL(hash1, hash2);
97+
}
98+
99+
// Goal: check that PC Hash Function generate correct hash
100+
BOOST_AUTO_TEST_CASE(pchash_reproduce_edge)
101+
{
102+
// pc_tag_hash = SHA256("PairCommit")
103+
uint256 pc_tag_hash;
104+
std::string pc_tag = "PairCommit";
105+
CSHA256().Write((const unsigned char*)pc_tag.data(), pc_tag.size()).Finalize(pc_tag_hash.begin());
106+
107+
FastRandomContext rng;
108+
// empty
109+
const valtype x1 = {};
110+
// 520 random bytes
111+
const valtype x2{rng.randbytes(520)};
112+
// CompactSize(0)
113+
const valtype x1_size = {0x00};
114+
// CompactSize(520)
115+
const valtype x2_size = {0xfd, 0x08, 0x02};
116+
117+
uint256 hash1 = PairCommitHash(x1, x2);
118+
119+
HashWriter ss;
120+
ss << Raw{pc_tag_hash}
121+
<< Raw{pc_tag_hash}
122+
<< Raw{x1_size}
123+
<< Raw{x1}
124+
<< Raw{x2_size}
125+
<< Raw{x2};
126+
127+
uint256 hash2 = ss.GetSHA256();
128+
129+
BOOST_CHECK_EQUAL(hash1, hash2);
130+
}
131+
132+
133+
namespace {
134+
135+
bool TapscriptCheck(const valtype& witVerifyScript, const std::vector<valtype>& witData)
136+
{
137+
// Build a taproot address...
138+
XOnlyPubKey key_inner{ParseHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")};
139+
TaprootBuilder builder;
140+
builder.Add(/*depth=*/0, witVerifyScript, TAPROOT_LEAF_TAPSCRIPT, /*track=*/true);
141+
builder.Finalize(key_inner);
142+
143+
CScriptWitness witness;
144+
witness.stack.insert(witness.stack.begin(), witData.begin(), witData.end());
145+
witness.stack.push_back(witVerifyScript);
146+
auto controlblock = *(builder.GetSpendData().scripts[{witVerifyScript, TAPROOT_LEAF_TAPSCRIPT}].begin());
147+
witness.stack.push_back(controlblock);
148+
149+
uint32_t flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_TAPROOT | SCRIPT_VERIFY_PAIRCOMMIT;
150+
CScript scriptPubKey = CScript() << OP_1 << ToByteVector(builder.GetOutput());
151+
152+
CMutableTransaction txFrom;
153+
txFrom.vout.resize(1);
154+
txFrom.vout[0].scriptPubKey = scriptPubKey;
155+
txFrom.vout[0].nValue = 10000;
156+
157+
CMutableTransaction txTo;
158+
txTo.vin.resize(1);
159+
txTo.vin[0].prevout.n = 0;
160+
txTo.vin[0].prevout.hash = txFrom.GetHash();
161+
txTo.vin[0].scriptWitness = witness;
162+
163+
PrecomputedTransactionData txdata(txTo);
164+
165+
return CScriptCheck(txFrom.vout[0], CTransaction(txTo), 0, flags, false, &txdata)();
166+
}
167+
}
168+
169+
// Goal: check that OP_PAIRCOMMIT behaves as expected in script
170+
BOOST_AUTO_TEST_CASE(pchash_tapscript)
171+
{
172+
CScript script;
173+
174+
// "Hello "
175+
const valtype x1 = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20};
176+
// "World!"
177+
const valtype x2 = {0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21};
178+
179+
// <expected_result> | <x1> <x2> OP_PAIRCOMMIT OP_EQUAL
180+
script
181+
<< ToByteVector(x1)
182+
<< ToByteVector(x2)
183+
<< OP_PAIRCOMMIT
184+
<< OP_EQUAL;
185+
186+
const valtype witVerifyScript = ToByteVector(script);
187+
188+
// Positive test: script must VERIFY with <test1_expected_result>
189+
const std::vector<valtype> witData1{ ToByteVector(uint256{test1_expected_result}) };
190+
191+
bool verify = TapscriptCheck(witVerifyScript, witData1);
192+
193+
BOOST_CHECK(verify);
194+
195+
// Negative test: script must FAIL with <test2_expected_result>
196+
const std::vector<valtype> witData2{ ToByteVector(uint256{test2_expected_result}) };
197+
198+
bool fail = !TapscriptCheck(witVerifyScript, witData2);
199+
200+
BOOST_CHECK(fail);
201+
}
202+
203+
BOOST_AUTO_TEST_SUITE_END()

Diff for: src/validation.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -2245,15 +2245,20 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const Ch
22452245
}
22462246

22472247
// Enforce CHECKSIGFROMSTACK(VERIFY) (BIN-2024-0003)
2248-
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_LN_HANCE)) {
2248+
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_LNHANCE)) {
22492249
flags |= SCRIPT_VERIFY_CHECKSIGFROMSTACK;
22502250
}
22512251

22522252
// Process INTERNALKEY (BIN-2024-0004)
2253-
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_LN_HANCE)) {
2253+
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_LNHANCE)) {
22542254
flags |= SCRIPT_VERIFY_INTERNALKEY;
22552255
}
22562256

2257+
// Process PAIRCOMMIT (BIN-2024-0006)
2258+
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_LNHANCE)) {
2259+
flags |= SCRIPT_VERIFY_PAIRCOMMIT;
2260+
}
2261+
22572262
// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
22582263
if (DeploymentActiveAt(block_index, chainman, Consensus::DEPLOYMENT_SEGWIT)) {
22592264
flags |= SCRIPT_VERIFY_NULLDUMMY;

0 commit comments

Comments
 (0)