Skip to content

Commit f3cecd2

Browse files
committed
Addendum: BIP65 (Lock Time) Implemented
Upgrades OP_NOP2 to OP_CHECKLOCKTIMEVERIFY and adds code to service this feature including unit tests. The default block version number will be incremented to 3 after the hard fork to indicate mandatory support for BIP65 and BIP66. Other versions above 1 are also accepted though.
1 parent fd37b73 commit f3cecd2

File tree

9 files changed

+489
-202
lines changed

9 files changed

+489
-202
lines changed

src/main.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInpu
811811
// Check against previous transactions
812812
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
813813
if(!tx.CheckInputs(view, CS_ALWAYS,
814-
SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_DERSIG)) {
814+
SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOCKTIME)) {
815815
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
816816
}
817817
}
@@ -1726,6 +1726,7 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view) {
17261726
bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
17271727

17281728
bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view) {
1729+
uint flags = SCRIPT_VERIFY_P2SH;
17291730
uint i;
17301731

17311732
/* Make sure the block index and coins DB are synchronised;
@@ -1759,6 +1760,14 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view) {
17591760
return(error("ConnectBlock() : transaction overwrite attempt detected"));
17601761
}
17611762

1763+
/* Enforce strict encoding, BIP66 (Strict DER), BIP65 (Lock Time) */
1764+
if((fTestNet && (pindex->nHeight > nTestnetForkSix)) ||
1765+
(!fTestNet && (pindex->nHeight > nForkSeven))) {
1766+
flags |= SCRIPT_VERIFY_STRICTENC;
1767+
flags |= SCRIPT_VERIFY_DERSIG;
1768+
flags |= SCRIPT_VERIFY_LOCKTIME;
1769+
}
1770+
17621771
CBlockUndo blockundo;
17631772

17641773
int64 nFees = 0, nValueIn = 0, nValueOut = 0, nActualStakeReward = 0;
@@ -1800,8 +1809,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view) {
18001809
nActualStakeReward = nTxValueOut - nTxValueIn;
18011810
} else nFees += nTxValueIn - nTxValueOut;
18021811

1803-
if(!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, SCRIPT_VERIFY_P2SH))
1804-
return false;
1812+
if(!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, flags))
1813+
return(false);
18051814
}
18061815

18071816
// don't create coinbase coins for proof-of-stake block
@@ -2484,14 +2493,15 @@ bool CBlock::AcceptBlock(CDiskBlockPos *dbp) {
24842493
}
24852494

24862495
/* Don't accept v1 blocks after this point */
2487-
if(!fTestNet && (nTime > nForkTwoTime)) {
2496+
if(fTestNet || (!fTestNet && (nTime > nForkTwoTime))) {
24882497
CScript expect = CScript() << nHeight;
24892498
if(!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin()))
24902499
return(DoS(100, error("AcceptBlock() : incorrect block height in coin base")));
24912500
}
24922501

2493-
/* Don't accept blocks with bogus version numbers after this point */
2494-
if((nHeight >= nForkSix) || (fTestNet && (nHeight >= nTestnetForkFive))) {
2502+
/* Don't accept blocks with bogus version numbers */
2503+
if((fTestNet && (nHeight >= nTestnetForkFive) && (nHeight <= nTestnetForkSix)) ||
2504+
(!fTestNet && (nHeight >= nForkSix) && (nHeight <= nForkSeven))) {
24952505
if(nVersion != 2)
24962506
return(DoS(100, error("AcceptBlock() : incorrect block version %u", nVersion)));
24972507
}

src/main.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ static const uint nTestnetForkTwoTime = 1473811200; /* 14 Sep 2016 00:00:00 G
8484

8585

8686
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
87-
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
88-
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
8987

9088
#ifdef USE_UPNP
9189
static const int fHaveUPnP = true;

src/miner.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,10 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64 *pStakeReward
246246
if ((tx.nTime > nAdjTime) || (fProofOfStake && tx.nTime > pblock->vtx[0].nTime))
247247
continue;
248248

249-
// Use advanced fee calculation after the chain switch
250-
if(fTestNet || (nAdjTime > nForkTwoTime)) {
251-
// Orbitcoin: low priority transactions up to 500 bytes in size
252-
// are free unless they get caught by the dust spam filter
253-
bool fAllowFree = ((nBlockSize + nTxSize < 1500) || CTransaction::AllowFree(dPriority));
254-
nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
255-
} else {
256-
nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK);
257-
}
249+
/* Low priority transactions up to 500 bytes in size
250+
* are free unless they get caught by the dust spam filter */
251+
bool fAllowFree = ((nBlockSize + nTxSize < 1500) || CTransaction::AllowFree(dPriority));
252+
nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
258253

259254
// Skip free transactions if we're past the minimum block size:
260255
if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
@@ -270,8 +265,10 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64 *pStakeReward
270265
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
271266
}
272267

273-
if(!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_P2SH))
274-
continue;
268+
/* Script verification has been passed already while accepting
269+
* transactions to the memory pool */
270+
if(!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_NONE))
271+
continue;
275272

276273
int64 nTxFees = tx.GetValueIn(viewTemp)-tx.GetValueOut();
277274
if (nTxFees < nMinFee)
@@ -341,6 +338,11 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64 *pStakeReward
341338
pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime()));
342339
if(!fProofOfStake) pblock->UpdateTime(pindexPrev);
343340
pblock->nNonce = 0;
341+
342+
if((fTestNet && (pindexPrev->nHeight >= nTestnetForkSix)) ||
343+
(!fTestNet && (pindexPrev->nHeight >= nForkSeven))) {
344+
pblock->nVersion = 3;
345+
}
344346
}
345347

346348
return(pblock);

src/script.cpp

Lines changed: 220 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ static const CBigNum bnZero(0);
2828
static const CBigNum bnOne(1);
2929
static const CBigNum bnFalse(0);
3030
static const CBigNum bnTrue(1);
31-
static const size_t nMaxNumSize = 4;
31+
static const size_t nDefaultMaxNumSize = 4;
3232

3333

34-
CBigNum CastToBigNum(const valtype& vch)
35-
{
34+
CBigNum CastToBigNum(const valtype &vch, const size_t nMaxNumSize = nDefaultMaxNumSize) {
35+
3636
if (vch.size() > nMaxNumSize)
3737
throw runtime_error("CastToBigNum() : overflow");
3838
// Get rid of extra leading zeros
@@ -228,10 +228,10 @@ const char* GetOpName(opcodetype opcode)
228228
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
229229
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
230230
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
231+
case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY"; /* former OP_NOP2 */
231232

232233
// expanson
233234
case OP_NOP1 : return "OP_NOP1";
234-
case OP_NOP2 : return "OP_NOP2";
235235
case OP_NOP3 : return "OP_NOP3";
236236
case OP_NOP4 : return "OP_NOP4";
237237
case OP_NOP5 : return "OP_NOP5";
@@ -480,10 +480,79 @@ bool EvalScript(vector<vector<uchar> > &stack, const CScript &script, const CTra
480480
//
481481
// Control
482482
//
483-
case OP_NOP:
484-
case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5:
485-
case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
486-
break;
483+
case(OP_NOP):
484+
break;
485+
486+
case(OP_CHECKLOCKTIMEVERIFY): {
487+
/* Treat as OP_NOP2 if disabled */
488+
if(!(flags & SCRIPT_VERIFY_LOCKTIME))
489+
break;
490+
491+
if(stack.size() < 1)
492+
return(false);
493+
494+
// Note that elsewhere numeric opcodes are limited to
495+
// operands in the range -2**31+1 to 2**31-1, however it is
496+
// legal for opcodes to produce results exceeding that
497+
// range. This limitation is implemented by CScriptNum's
498+
// default 4-byte limit.
499+
//
500+
// If we kept to that limit we'd have a year 2038 problem,
501+
// even though the nLockTime field in transactions
502+
// themselves is uint32 which only becomes meaningless
503+
// after the year 2106.
504+
//
505+
// Thus as a special case we tell CastToBigNum to accept up
506+
// to 5-byte bignums, which are good until 2**39-1, well
507+
// beyond the 2**32-1 limit of the nLockTime field itself.
508+
const CBigNum nLockTime = CastToBigNum(stacktop(-1), 5);
509+
510+
// In the rare event that the argument may be < 0 due to
511+
// some arithmetic being done first, you can always use
512+
// 0 MAX CHECKLOCKTIMEVERIFY.
513+
if(nLockTime < 0)
514+
return(false);
515+
516+
// There are two times of nLockTime: lock-by-blockheight
517+
// and lock-by-blocktime, distinguished by whether
518+
// nLockTime < LOCKTIME_THRESHOLD.
519+
//
520+
// We want to compare apples to apples, so fail the script
521+
// unless the type of nLockTime being tested is the same as
522+
// the nLockTime in the transaction.
523+
if(!(((txTo.nLockTime < LOCKTIME_THRESHOLD) && (nLockTime < LOCKTIME_THRESHOLD)) ||
524+
((txTo.nLockTime >= LOCKTIME_THRESHOLD) && (nLockTime >= LOCKTIME_THRESHOLD))))
525+
return(false);
526+
527+
// Now that we know we're comparing apples-to-apples,
528+
// the comparison is a simple numeric one.
529+
if(nLockTime > (int64_t)txTo.nLockTime)
530+
return(false);
531+
532+
// Finally the nLockTime feature can be disabled and thus
533+
// CHECKLOCKTIMEVERIFY bypassed if every txin has been
534+
// finalized by setting nSequence to maxint. The
535+
// transaction would be allowed into the blockchain, making
536+
// the opcode ineffective.
537+
//
538+
// Testing if this vin is not final is sufficient to
539+
// prevent this condition. Alternatively we could test all
540+
// inputs, but testing just this input minimizes the data
541+
// required to prove correct CHECKLOCKTIMEVERIFY execution.
542+
if(txTo.vin[nIn].IsFinal())
543+
return(false);
544+
} break;
545+
546+
case(OP_NOP1):
547+
case(OP_NOP3):
548+
case(OP_NOP4):
549+
case(OP_NOP5):
550+
case(OP_NOP6):
551+
case(OP_NOP7):
552+
case(OP_NOP8):
553+
case(OP_NOP9):
554+
case(OP_NOP10):
555+
break;
487556

488557
case OP_IF:
489558
case OP_NOTIF:
@@ -2201,3 +2270,146 @@ bool CScriptCompressor::Decompress(unsigned int nSize, const std::vector<unsigne
22012270
}
22022271
return false;
22032272
}
2273+
2274+
2275+
#include <boost/assign/list_of.hpp>
2276+
2277+
const map<uchar, string> mapSigHashTypes = boost::assign::map_list_of
2278+
(static_cast<uchar>(SIGHASH_ALL), string("ALL"))
2279+
(static_cast<uchar>(SIGHASH_ALL|SIGHASH_ANYONECANPAY), string("ALL|ANYONECANPAY"))
2280+
(static_cast<uchar>(SIGHASH_NONE), string("NONE"))
2281+
(static_cast<uchar>(SIGHASH_NONE|SIGHASH_ANYONECANPAY), string("NONE|ANYONECANPAY"))
2282+
(static_cast<uchar>(SIGHASH_SINGLE), string("SINGLE"))
2283+
(static_cast<unsigned char>(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), string("SINGLE|ANYONECANPAY"));
2284+
2285+
/**
2286+
* Create the assembly string representation of a CScript object.
2287+
* @param[in] script CScript object to convert into the asm string representation.
2288+
* @param[in] fAttemptSighashDecode Whether to attempt to decode sighash types on data within the script that matches the format
2289+
* of a signature. Only pass true for scripts you believe could contain signatures. For example,
2290+
* pass false, or omit the this argument (defaults to false), for scriptPubKeys.
2291+
*/
2292+
std::string ScriptToAsmStr(const CScript &script, const bool fAttemptSighashDecode) {
2293+
std::string str;
2294+
opcodetype opcode;
2295+
vector<uchar> vch;
2296+
2297+
CScript::const_iterator pc = script.begin();
2298+
while(pc < script.end()) {
2299+
if(!str.empty()) {
2300+
str += " ";
2301+
}
2302+
if(!script.GetOp(pc, opcode, vch)) {
2303+
str += "[error]";
2304+
return(str);
2305+
}
2306+
if((0 <= opcode) && (opcode <= OP_PUSHDATA4)) {
2307+
if(vch.size() <= static_cast<vector<uchar>::size_type>(4)) {
2308+
str += strprintf("%d", CBigNum(vch).getint());
2309+
} else {
2310+
// the IsUnspendable check makes sure not to try to decode OP_RETURN data that may match the format of a signature
2311+
if(fAttemptSighashDecode && !script.IsUnspendable()) {
2312+
string strSigHashDecode;
2313+
// goal: only attempt to decode a defined sighash type from data that looks like a signature within a scriptSig.
2314+
// this won't decode correctly formatted public keys in Pubkey or Multisig scripts due to
2315+
// the restrictions on the pubkey formats (see IsCompressedOrUncompressedPubKey) being incongruous with the
2316+
// checks in CheckSignatureEncoding.
2317+
if(CheckSignatureEncoding(vch, SCRIPT_VERIFY_STRICTENC)) {
2318+
const uchar chSigHashType = vch.back();
2319+
if(mapSigHashTypes.count(chSigHashType)) {
2320+
strSigHashDecode = "[" + mapSigHashTypes.find(chSigHashType)->second + "]";
2321+
vch.pop_back(); // remove the sighash type byte. it will be replaced by the decode.
2322+
}
2323+
}
2324+
str += HexStr(vch) + strSigHashDecode;
2325+
} else {
2326+
str += HexStr(vch);
2327+
}
2328+
}
2329+
} else {
2330+
str += GetOpName(opcode);
2331+
}
2332+
}
2333+
return(str);
2334+
}
2335+
2336+
#include <boost/algorithm/string/classification.hpp>
2337+
#include <boost/algorithm/string/predicate.hpp>
2338+
#include <boost/algorithm/string/replace.hpp>
2339+
#include <boost/algorithm/string/split.hpp>
2340+
2341+
static map<std::string, opcodetype> mapOpNames;
2342+
2343+
/* Script parser for unit tests;
2344+
* fills out CScript from std:string, returns 1 on success or 0 on failure */
2345+
bool ParseScript(const std::string &source, CScript &result) {
2346+
uint op;
2347+
2348+
/* One time action */
2349+
if(mapOpNames.empty()) {
2350+
const char *name;
2351+
std::string strName;
2352+
2353+
/* Process opcode range [0x61:0xB9] */
2354+
for(op = OP_NOP; op <= OP_NOP10; op++) {
2355+
name = GetOpName((opcodetype)op);
2356+
if(!strcmp(name, "OP_UNKNOWN"))
2357+
continue;
2358+
2359+
strName = std::string(name);
2360+
mapOpNames[strName] = (opcodetype)op;
2361+
2362+
/* Opcodes are recognised with or without OP_ */
2363+
boost::algorithm::replace_first(strName, "OP_", "");
2364+
mapOpNames[strName] = (opcodetype)op;
2365+
}
2366+
/* Insert OP_RESERVED (0x50) */
2367+
mapOpNames[std::string("OP_RESERVED")] = (opcodetype)0x50;
2368+
mapOpNames[std::string("RESERVED")] = (opcodetype)0x50;
2369+
/* Insert OP_NOP2 (0xB1); upgraded to OP_CHECKLOCKTIMEVERIFY */
2370+
mapOpNames[std::string("OP_NOP2")] = (opcodetype)0xB1;
2371+
mapOpNames[std::string("NOP2")] = (opcodetype)0xB1;
2372+
}
2373+
2374+
vector<string> words;
2375+
boost::algorithm::split(words, source, boost::algorithm::is_any_of(" \t\n"),
2376+
boost::algorithm::token_compress_on);
2377+
2378+
for(std::vector<std::string>::const_iterator w = words.begin(); w != words.end(); w++) {
2379+
if(w->empty()) {
2380+
/* Empty string, ignore; boost::split given '' will return one word */
2381+
continue;
2382+
}
2383+
if(all(*w, boost::algorithm::is_digit()) ||
2384+
((boost::algorithm::starts_with(*w, "-") &&
2385+
all(string(w->begin() + 1, w->end()), boost::algorithm::is_digit())))) {
2386+
/* Number */
2387+
int64 n = atoi64(*w);
2388+
result << n;
2389+
continue;
2390+
}
2391+
if(boost::algorithm::starts_with(*w, "0x") && ((w->begin() + 2) != w->end()) &&
2392+
IsHex(string(w->begin() + 2, w->end()))) {
2393+
/* Raw hex data, inserted NOT pushed into stack */
2394+
std::vector<uchar> raw = ParseHex(string(w->begin() + 2, w->end()));
2395+
result.insert(result.end(), raw.begin(), raw.end());
2396+
continue;
2397+
}
2398+
if((w->size() >= 2) && boost::algorithm::starts_with(*w, "'") &&
2399+
boost::algorithm::ends_with(*w, "'")) {
2400+
/* Single-quoted string, pushed as data. NOTE: this is poor-man's
2401+
* parsing, spaces/tabs/newlines in single-quoted strings won't work */
2402+
std::vector<uchar> value(w->begin() + 1, w->end() - 1);
2403+
result << value;
2404+
continue;
2405+
}
2406+
if(mapOpNames.count(*w)) {
2407+
/* Opcode */
2408+
result << mapOpNames[*w];
2409+
continue;
2410+
}
2411+
return(false);
2412+
}
2413+
2414+
return(true);
2415+
}

0 commit comments

Comments
 (0)