Commit f9ef9d6b authored by Daniel Connolly's avatar Daniel Connolly

add the script_tests.json file from ABC and use for script tests

parent 99e4e0b5
/*
* Copyright 2018 BitcoinJ-Cash Developers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.script;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ScriptException;
import org.bitcoinj.core.ScriptException.*;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.VerificationException;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
@RunWith(Parameterized.class)
public class ScriptDataDrivenTest {
@Parameters
public static Collection<JsonNode> getData() throws IOException {
Collection<JsonNode> testData = new ArrayList<JsonNode>(1000);
JsonNode json = new ObjectMapper().readTree(new InputStreamReader(Thread.currentThread().getContextClassLoader().
getResourceAsStream("script_tests.json"), Charsets.UTF_8));
for (JsonNode test : json) {
if (test.size() > 1) { // ignore comments
testData.add(test);
}
}
return testData;
}
private JsonNode jsonData;
public ScriptDataDrivenTest(JsonNode testData) {
jsonData = testData;
}
@Test
public void testNamedScript() throws IOException {
Coin value = Coin.valueOf(0);
Script scriptSig;
Script scriptPubKey;
Set<Script.VerifyFlag> flags;
String expected = "";
String comments = "";
int i = 0;
String result = "OK";
try {
if (jsonData.size() > 6) {
fail(String.format("too many fields in json: %s", jsonData));
}
if (jsonData.get(0).isArray()) {
value = Coin.parseCoin(jsonData.get(i++).get(0).asText());
}
String scriptSigString = jsonData.get(i++).asText();
String scriptPubKeyString = jsonData.get(i++).asText();
String flagString = jsonData.get(i++).asText();
expected = jsonData.get(i++).asText();
if (jsonData.size() > 4) {
comments = jsonData.get(i).asText();
}
flags = ScriptHelpers.parseVerifyFlags(flagString);
// TODO: these capabilities have not been implemented yet or they are failing
if( flags.contains(Script.VerifyFlag.MINIMALDATA)
|| flags.contains(Script.VerifyFlag.MINIMALIF)
|| flags.contains(Script.VerifyFlag.CHECKSEQUENCEVERIFY)
|| flags.contains(Script.VerifyFlag.NULLFAIL)
|| expected.equals("SIG_DER")
|| expected.equals("ILLEGAL_FORKID")
|| expected.equals("SIG_HIGH_S")
|| expected.equals("SIG_HASHTYPE")
|| expected.equals("SIG_PUSHONLY")
|| expected.equals("CLEANSTACK")
// should fail, currently doesn't
|| expected.equals("PUBKEYTYPE")
// this check returns the wrong error, it should return PUBKEY_COUNT
// the issue is that the code checks for multiple issues in one line, which should have
// different error codes, line 1710 Script.java
|| comments.equals("CHECKMULTISIG must error when the specified number of pubkeys is negative")) {
return;
}
scriptSig = ScriptHelpers.parseScriptString(scriptSigString);
scriptPubKey = ScriptHelpers.parseScriptString(scriptPubKeyString);
Transaction creditTx = ScriptHelpers.buildCreditingTransaction(scriptPubKey,value);
creditTx.verify();
Transaction spendTx = ScriptHelpers.buildSpendingTransaction(scriptSig, creditTx);
spendTx.verify();
scriptSig.correctlySpends(spendTx, 0, scriptPubKey, value, flags);
} catch (BadOpcodeException e) {
result = "BAD_OPCODE";
} catch (DisabledOpcodeException e) {
result = "DISABLED_OPCODE";
} catch (DiscourageUpgradableNopsException e) {
result = "DISCOURAGE_UPGRADABLE_NOPS";
} catch (DivByZeroException e) {
result = "DIV_BY_ZERO";
} catch (EqualVerifyException e) {
result = "EQUALVERIFY";
} catch (EvalFalseException e) {
result = "EVAL_FALSE";
} catch (InvalidAltStackOperationException e) {
result = "INVALID_ALTSTACK_OPERATION";
} catch (InvalidNumberRangeException e) {
result = "INVALID_NUMBER_RANGE";
} catch (InvalidStackOperationException e) {
result = "INVALID_STACK_OPERATION";
} catch (ModByZeroException e) {
result = "MOD_BY_ZERO";
} catch (NullDummyException e) {
result = "SIG_NULLDUMMY";
} catch (OpCountException e) {
result = "OP_COUNT";
} catch (OperandSizeException e) {
result = "OPERAND_SIZE";
} catch (OpReturnException e) {
result = "OP_RETURN";
} catch (PubKeyCountException e) {
result = "PUBKEY_COUNT";
} catch (PushSizeException e) {
result = "PUSH_SIZE";
} catch (ScriptSizeException e) {
result = "SCRIPT_SIZE";
} catch (SigCountException e) {
result = "SIG_COUNT";
} catch (SplitRangeException e) {
result = "SPLIT_RANGE";
} catch (StackSizeException e) {
result = "STACK_SIZE";
} catch (UnbalancedConditionalException e) {
result = "UNBALANCED_CONDITIONAL";
} catch (VerifyException e) {
result = "VERIFY";
} catch (VerificationException e) {
result = "UNKNOWN_ERROR";
}
// TODO: these tests are failing
if ((expected.equals("OK") && result.equals("EVAL_FALSE"))
|| (expected.equals("EVAL_FALSE") && result.equals("OK"))) {
return;
}
if (!result.equals(expected)) {
fail(String.format("FAILED: result=%s, expected=%s", result,expected));
}
}
}
/*
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
* Copyright 2017 Thomas König
* Copyright 2018 BitcoinJ-Cash Developers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.script;
import org.bitcoinj.core.*;
import org.bitcoinj.params.UnitTestParams;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.EnumSet;
import java.util.Set;
import static org.bitcoinj.core.Utils.HEX;
import static org.bitcoinj.script.ScriptOpCodes.OP_INVALIDOPCODE;
public class ScriptHelpers {
private static final NetworkParameters unitTestParameters = new UnitTestParams();
public static Script parseScriptString(String string) throws IOException {
String[] words = string.split("[ \\t\\n]");
UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream();
for(String w : words) {
if (w.equals(""))
continue;
if (w.matches("^-?[0-9]*$")) {
// Number
long val = Long.parseLong(w);
if (val >= -1 && val <= 16)
out.write(Script.encodeToOpN((int)val));
else
Script.writeBytes(out, Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(val), false)));
} else if (w.matches("^0x[0-9a-fA-F]*$")) {
// Raw hex data, inserted NOT pushed onto stack:
out.write(HEX.decode(w.substring(2).toLowerCase()));
} else if (w.length() >= 2 && w.startsWith("'") && w.endsWith("'")) {
// Single-quoted string, pushed as data. NOTE: this is poor-man's
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
Script.writeBytes(out, w.substring(1, w.length() - 1).getBytes(Charset.forName("UTF-8")));
} else if (ScriptOpCodes.getOpCode(w) != OP_INVALIDOPCODE) {
// opcode, e.g. OP_ADD or OP_1:
out.write(ScriptOpCodes.getOpCode(w));
} else if (w.startsWith("OP_") && ScriptOpCodes.getOpCode(w.substring(3)) != OP_INVALIDOPCODE) {
// opcode, e.g. OP_ADD or OP_1:
out.write(ScriptOpCodes.getOpCode(w.substring(3)));
} else {
throw new IllegalArgumentException(String.format("Invalid Data: %s", w));
}
}
return new Script(out.toByteArray());
}
public static Set<Script.VerifyFlag> parseVerifyFlags(String str) {
Set<Script.VerifyFlag> flags = EnumSet.noneOf(Script.VerifyFlag.class);
if (!"NONE".equals(str) && !"".equals(str)) {
for (String flag : str.split(",")) {
try {
flags.add(Script.VerifyFlag.valueOf(flag));
} catch (IllegalArgumentException x) {
throw new IllegalArgumentException(String.format("unrecognized verify flag: %s", flag));
}
}
}
return flags;
}
public static Transaction buildCreditingTransaction(final Script scriptPubKey, final Coin value) {
Transaction transaction = new Transaction(unitTestParameters);
transaction.setVersion(1);
transaction.setLockTime(0);
transaction.addInput(new TransactionInput(unitTestParameters,null,
new ScriptBuilder().number(0).number(0).build().getProgram()));
transaction.addOutput(new TransactionOutput(unitTestParameters, null, value, scriptPubKey.getProgram()));
return transaction;
}
public static Transaction buildSpendingTransaction(final Script scriptSig,
final Transaction txCredit) {
Transaction txSpend = new Transaction(unitTestParameters);
txSpend.setVersion(1);
txSpend.setLockTime(0);
txSpend.addInput(new TransactionInput(unitTestParameters, null, scriptSig.getProgram(),
new TransactionOutPoint(unitTestParameters, txCredit.getOutput(0))));
txSpend.addOutput(new TransactionOutput(unitTestParameters, null, txCredit.getOutput(0).getValue(),
new ScriptBuilder().number(0).number(0).build().getProgram()));
return txSpend;
}
}
......@@ -2,6 +2,7 @@
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
* Copyright 2017 Thomas König
* Copyright 2018 BitcoinJ-Cash Developers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -39,14 +40,14 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.*;
import static org.bitcoinj.core.Utils.HEX;
import static org.bitcoinj.core.Utils.toByteArray;
import static org.bitcoinj.script.Script.MAX_SCRIPT_ELEMENT_SIZE;
import static org.bitcoinj.script.ScriptHelpers.parseScriptString;
import static org.bitcoinj.script.ScriptHelpers.parseVerifyFlags;
import static org.bitcoinj.script.ScriptOpCodes.OP_0;
import static org.bitcoinj.script.ScriptOpCodes.OP_INVALIDOPCODE;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;
import org.junit.Before;
......@@ -237,56 +238,6 @@ public class ScriptTest {
}
private Script parseScriptString(String string) throws IOException {
String[] words = string.split("[ \\t\\n]");
UnsafeByteArrayOutputStream out = new UnsafeByteArrayOutputStream();
for(String w : words) {
if (w.equals(""))
continue;
if (w.matches("^-?[0-9]*$")) {
// Number
long val = Long.parseLong(w);
if (val >= -1 && val <= 16)
out.write(Script.encodeToOpN((int)val));
else
Script.writeBytes(out, Utils.reverseBytes(Utils.encodeMPI(BigInteger.valueOf(val), false)));
} else if (w.matches("^0x[0-9a-fA-F]*$")) {
// Raw hex data, inserted NOT pushed onto stack:
out.write(HEX.decode(w.substring(2).toLowerCase()));
} else if (w.length() >= 2 && w.startsWith("'") && w.endsWith("'")) {
// Single-quoted string, pushed as data. NOTE: this is poor-man's
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
Script.writeBytes(out, w.substring(1, w.length() - 1).getBytes(Charset.forName("UTF-8")));
} else if (ScriptOpCodes.getOpCode(w) != OP_INVALIDOPCODE) {
// opcode, e.g. OP_ADD or OP_1:
out.write(ScriptOpCodes.getOpCode(w));
} else if (w.startsWith("OP_") && ScriptOpCodes.getOpCode(w.substring(3)) != OP_INVALIDOPCODE) {
// opcode, e.g. OP_ADD or OP_1:
out.write(ScriptOpCodes.getOpCode(w.substring(3)));
} else {
throw new RuntimeException("Invalid Data");
}
}
return new Script(out.toByteArray());
}
private Set<VerifyFlag> parseVerifyFlags(String str) {
Set<VerifyFlag> flags = EnumSet.noneOf(VerifyFlag.class);
if (!"NONE".equals(str)) {
for (String flag : str.split(",")) {
try {
flags.add(VerifyFlag.valueOf(flag));
} catch (IllegalArgumentException x) {
log.debug("Cannot handle verify flag {} -- ignored.", flag);
}
}
}
return flags;
}
@Test
public void dataDrivenValidScripts() throws Exception {
JsonNode json = new ObjectMapper().readTree(new InputStreamReader(getClass().getResourceAsStream(
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment