mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-01-30 23:02:15 +00:00
Count P2SH SigOps the way the reference client does.
This commit is contained in:
parent
7ca87c078c
commit
c789b757f3
@ -99,7 +99,9 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
blockStore.beginDatabaseBatchWrite();
|
||||
|
||||
LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
|
||||
LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>();
|
||||
LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>();
|
||||
long sigOps = 0;
|
||||
boolean enforceBIP16 = block.getTimeSeconds() >= params.BIP16_ENFORCE_TIME;
|
||||
try {
|
||||
if (!params.isCheckpoint(height)) {
|
||||
// BIP30 violator blocks are ones that contain a duplicated transaction. They are all in the
|
||||
@ -111,6 +113,13 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
// being added twice (bug) or the block is a BIP30 violator.
|
||||
if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
|
||||
throw new VerificationException("Block failed BIP30 test!");
|
||||
if (enforceBIP16) { // We already check non-BIP16 sigops in Block.verifyTransactions(true)
|
||||
try {
|
||||
sigOps += tx.getSigOpCount();
|
||||
} catch (ScriptException e) {
|
||||
throw new VerificationException("Invalid script in transaction");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Transaction tx : block.transactions) {
|
||||
@ -124,6 +133,16 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
if (prevOut == null)
|
||||
throw new VerificationException("Attempted to spend a non-existent or already spent output!");
|
||||
// TODO: Check we're not spending the genesis transaction here. Satoshis code won't allow it.
|
||||
if (enforceBIP16) {
|
||||
try {
|
||||
if (new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length).isPayToScriptHash())
|
||||
sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
|
||||
} catch (ScriptException e) {
|
||||
throw new VerificationException("Error reading script in transaction");
|
||||
}
|
||||
if (sigOps > Block.MAX_BLOCK_SIGOPS)
|
||||
throw new VerificationException("Too many P2SH SigOps in block");
|
||||
}
|
||||
//TODO: check script here
|
||||
blockStore.removeUnspentTransactionOutput(prevOut);
|
||||
txOutsSpent.add(prevOut);
|
||||
@ -170,8 +189,9 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
if (transactions != null) {
|
||||
LinkedList<StoredTransactionOutput> txOutsSpent = new LinkedList<StoredTransactionOutput>();
|
||||
LinkedList<StoredTransactionOutput> txOutsCreated = new LinkedList<StoredTransactionOutput>();
|
||||
long sigOps = 0;
|
||||
boolean enforcePayToScriptHash = newBlock.getHeader().getTimeSeconds() >= params.BIP16_ENFORCE_TIME;
|
||||
if (!params.isCheckpoint(newBlock.getHeight())) {
|
||||
// See explanation above.
|
||||
for(StoredTransaction tx : transactions) {
|
||||
Sha256Hash hash = tx.getHash();
|
||||
if (blockStore.hasUnspentOutputs(hash, tx.getOutputs().size()))
|
||||
@ -186,6 +206,17 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
|
||||
in.getOutpoint().getIndex());
|
||||
if (prevOut == null)
|
||||
throw new VerificationException("Attempted spend of a non-existent or already spent output!");
|
||||
if (enforcePayToScriptHash) {
|
||||
try {
|
||||
Script script = new Script(params, prevOut.getScriptBytes(), 0, prevOut.getScriptBytes().length);
|
||||
if (script.isPayToScriptHash())
|
||||
sigOps += Script.getP2SHSigOpCount(in.getScriptBytes());
|
||||
} catch (ScriptException e) {
|
||||
throw new VerificationException("Error reading script in transaction");
|
||||
}
|
||||
if (sigOps > Block.MAX_BLOCK_SIGOPS)
|
||||
throw new VerificationException("Too many P2SH SigOps in block");
|
||||
}
|
||||
//TODO: check script here
|
||||
blockStore.removeUnspentTransactionOutput(prevOut);
|
||||
txOutsSpent.add(prevOut);
|
||||
|
@ -153,6 +153,13 @@ public class NetworkParameters implements Serializable {
|
||||
public static final int TARGET_SPACING = 10 * 60; // 10 minutes per block.
|
||||
public static final int INTERVAL = TARGET_TIMESPAN / TARGET_SPACING;
|
||||
|
||||
/**
|
||||
* Blocks with a timestamp after this should enforce BIP 16, aka "Pay to script hash". This BIP changed the
|
||||
* network rules in a soft-forking manner, that is, blocks that don't follow the rules are accepted but not
|
||||
* mined upon and thus will be quickly re-orged out as long as the majority are enforcing the rule.
|
||||
*/
|
||||
public final int BIP16_ENFORCE_TIME = 1333238400;
|
||||
|
||||
/**
|
||||
* The maximum money to be generated
|
||||
*/
|
||||
|
@ -751,4 +751,43 @@ public class Script {
|
||||
}
|
||||
return getSigOpCount(script.chunks, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of P2SH Sig Ops in the Script scriptSig
|
||||
*/
|
||||
public static long getP2SHSigOpCount(byte[] scriptSig) throws ScriptException {
|
||||
Script script = new Script();
|
||||
try {
|
||||
script.parse(scriptSig, 0, scriptSig.length);
|
||||
} catch (ScriptException e) {
|
||||
// Ignore errors and count up to the parse-able length
|
||||
}
|
||||
for (int i = script.chunks.size() - 1; i >= 0; i--)
|
||||
if (!script.chunks.get(i).isOpCode) {
|
||||
Script subScript = new Script();
|
||||
subScript.parse(script.chunks.get(i).data, 0, script.chunks.get(i).data.length);
|
||||
return getSigOpCount(subScript.chunks, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Whether or not this is a scriptPubKey representing a pay-to-script-hash output. In such outputs, the logic that
|
||||
* controls reclamation is not actually in the output at all. Instead there's just a hash, and it's up to the
|
||||
* spending input to provide a program matching that hash. This rule is "soft enforced" by the network as it does
|
||||
* not exist in Satoshis original implementation. It means blocks containing P2SH transactions that don't match
|
||||
* correctly are considered valid, but won't be mined upon, so they'll be rapidly re-orgd out of the chain. This
|
||||
* logic is defined by <a href="https://en.bitcoin.it/wiki/BIP_0016">BIP 16</a>.</p>
|
||||
*
|
||||
* <p>bitcoinj does not support creation of P2SH transactions today. The goal of P2SH is to allow short addresses
|
||||
* even for complex scripts (eg, multi-sig outputs) so they are convenient to work with in things like QRcodes or
|
||||
* with copy/paste, and also to minimize the size of the unspent output set (which improves performance of the
|
||||
* Bitcoin system).</p>
|
||||
*/
|
||||
public boolean isPayToScriptHash() {
|
||||
return program.length == 23 &&
|
||||
(program[0] & 0xff) == OP_HASH160 &&
|
||||
(program[1] & 0xff) == 0x14 &&
|
||||
(program[22] & 0xff) == OP_EQUAL;
|
||||
}
|
||||
}
|
||||
|
@ -919,7 +919,13 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this transaction is considered finalized and can be placed in a block
|
||||
* <p>Returns true if this transaction is considered finalized and can be placed in a block. Non-finalized
|
||||
* transactions won't be included by miners and can be replaced with newer versions using sequence numbers.
|
||||
* This is useful in certain types of <a href="http://en.bitcoin.it/wiki/Contracts">contracts</a>, such as
|
||||
* micropayment channels.</p>
|
||||
*
|
||||
* <p>Note that currently the replacement feature is disabled in the Satoshi client and will need to be
|
||||
* re-activated before this functionality is useful.</p>
|
||||
*/
|
||||
public boolean isFinal(int height, long blockTimeSeconds) {
|
||||
// Time based nLockTime implemented in 0.1.6
|
||||
|
Loading…
Reference in New Issue
Block a user