Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New redeem test #67

Open
wants to merge 4 commits into
base: segwit-poc
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>co.rsk.bitcoinj</groupId>
<version>0.14.4-rsk-14</version>
<version>0.14.4-rsk-14-SNAPSHOT</version>
<artifactId>bitcoinj-thin</artifactId>

<name>bitcoinj-thin</name>
Expand Down
43 changes: 42 additions & 1 deletion src/main/java/co/rsk/bitcoinj/core/TransactionWitness.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,55 @@ public static TransactionWitness createWitness(@Nullable final TransactionSignat

public static TransactionWitness createWitnessScript(Script witnessScript, List<TransactionSignature> signatures) {
List<byte[]> pushes = new ArrayList<>(signatures.size() + 2);
pushes.add(new byte[] {});
//pushes.add(new byte[] {});
for (TransactionSignature signature : signatures) {
pushes.add(signature.encodeToBitcoin());
}
pushes.add(witnessScript.getProgram());
return TransactionWitness.of(pushes);
}

public static TransactionWitness createWitnessScriptWithNewRedeem(Script witnessScript, List<TransactionSignature> thresholdSignatures, int signaturesSize) {
int zeroSignaturesSize = signaturesSize - thresholdSignatures.size();
List<byte[]> pushes = new ArrayList<>(signaturesSize + 1);
for (int i = 0; i < thresholdSignatures.size(); i++) {
pushes.add(thresholdSignatures.get(i).encodeToBitcoin());
}
for (int i = 0; i < zeroSignaturesSize; i ++) {
pushes.add(new byte[0]);
}
pushes.add(witnessScript.getProgram());
return TransactionWitness.of(pushes);
}

public static TransactionWitness createWitnessErpScriptWithNewRedeemStandard(Script witnessScript, List<TransactionSignature> thresholdSignatures, int signaturesSize) {
int zeroSignaturesSize = signaturesSize - thresholdSignatures.size();
List<byte[]> pushes = new ArrayList<>(signaturesSize + 2);
for (int i = 0; i < thresholdSignatures.size(); i++) {
pushes.add(thresholdSignatures.get(i).encodeToBitcoin());
}
for (int i = 0; i < zeroSignaturesSize; i ++) {
pushes.add(new byte[0]);
}
pushes.add(new byte[] {}); // OP_NOTIF argument
pushes.add(witnessScript.getProgram());
return TransactionWitness.of(pushes);
}

public static TransactionWitness createWitnessErpScriptWithNewRedeemEmergency(Script witnessScript, List<TransactionSignature> thresholdSignatures, int signaturesSize) {
int zeroSignaturesSize = signaturesSize - thresholdSignatures.size();
List<byte[]> pushes = new ArrayList<>(signaturesSize + 2);
for (int i = 0; i < thresholdSignatures.size(); i++) {
pushes.add(thresholdSignatures.get(i).encodeToBitcoin());
}
for (int i = 0; i < zeroSignaturesSize; i ++) {
pushes.add(new byte[0]);
}
pushes.add(new byte[] {1}); // OP_NOTIF argument
pushes.add(witnessScript.getProgram());
return TransactionWitness.of(pushes);
}

public static TransactionWitness createWitnessErpScript(Script witnessScript, List<TransactionSignature> signatures) {
List<byte[]> pushes = new ArrayList<>(signatures.size() + 3);
pushes.add(new byte[] {});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,43 @@ public static Script createP2shP2wshErpRedeemScriptWithFlyover(
return erpP2shP2wshRedeemScript;
}

public static Script createErpP2shP2wshNewRedeemScript(
Script defaultFederationRedeemScript,
Script erpFederationRedeemScript,
Long csvValue) {

/* validateErpRedeemScriptValues(
defaultFederationRedeemScript,
erpFederationRedeemScript,
csvValue
);*/
byte[] serializedCsvValue = Utils.signedLongToByteArrayLE(csvValue);

ScriptBuilder scriptBuilder = new ScriptBuilder();

Script erpRedeemScript = scriptBuilder
.op(ScriptOpCodes.OP_NOTIF)
.addChunks(defaultFederationRedeemScript.getChunks())
.op(ScriptOpCodes.OP_ELSE)
.data(serializedCsvValue)
.op(ScriptOpCodes.OP_CHECKSEQUENCEVERIFY)
.op(ScriptOpCodes.OP_DROP)
.addChunks(erpFederationRedeemScript.getChunks())
.op(ScriptOpCodes.OP_ENDIF)
.build();

/* // Validate the created redeem script has a valid structure
if (!RedeemScriptValidator.hasErpRedeemScriptStructure(erpRedeemScript.getChunks())) {
String message = String.format(
"Created redeem script has an invalid structure, not ERP redeem script. Redeem script created: %s",
erpRedeemScript
);
logger.debug("[createErpRedeemScript] {}", message);
throw new VerificationException(message);
}*/
return erpRedeemScript;
}

public static boolean isP2shErpFed(List<ScriptChunk> chunks) {
return RedeemScriptValidator.hasP2shErpRedeemScriptStructure(chunks);
}
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/co/rsk/bitcoinj/script/ScriptBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,25 @@ public static Script createMultiSigOutputScript(int threshold, List<BtcECKey> pu
return builder.build();
}

/** Creates a program that requires at least N of the given keys to sign, using OP_CHECKSIG, OP_SWAP and OP_ADD. */
public static Script createNewMultiSigOutputScript(int threshold, List<BtcECKey> pubkeys) {
checkArgument(threshold > 0);
checkArgument(threshold <= pubkeys.size());
ScriptBuilder builder = new ScriptBuilder();
BtcECKey lastKey = pubkeys.get(pubkeys.size() - 1);
builder.data(lastKey.getPubKey());
builder.op(OP_CHECKSIG);
for (int i = pubkeys.size() - 2; i >= 0; i --) {
builder.op(OP_SWAP);
builder.data(pubkeys.get(i).getPubKey());
builder.op(OP_CHECKSIG);
builder.op(OP_ADD);
}
builder.number(threshold);
builder.op(OP_NUMEQUAL);
return builder.build();
}

/** Create a program that satisfies an OP_CHECKMULTISIG program. */
public static Script createMultiSigInputScript(List<TransactionSignature> signatures) {
List<byte[]> sigs = new ArrayList<byte[]>(signatures.size());
Expand Down Expand Up @@ -448,6 +467,12 @@ public static Script createRedeemScript(int threshold, List<BtcECKey> pubkeys) {
return ScriptBuilder.createMultiSigOutputScript(threshold, pubkeys);
}

public static Script createNewRedeemScript(int threshold, List<BtcECKey> pubkeys) {
pubkeys = new ArrayList<BtcECKey>(pubkeys);
Collections.sort(pubkeys, BtcECKey.PUBKEY_COMPARATOR);
return ScriptBuilder.createNewMultiSigOutputScript(threshold, pubkeys);
}

/**
* Creates a script of the form OP_RETURN [data]. This feature allows you to attach a small piece of data (like
* a hash of something stored elsewhere) to a zero valued output which can never be spent and thus does not pollute
Expand Down
100 changes: 100 additions & 0 deletions src/test/java/co/rsk/bitcoinj/script/NewRedeemScriptTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package co.rsk.bitcoinj.script;

import static co.rsk.bitcoinj.script.Script.ALL_VERIFY_FLAGS;

import co.rsk.bitcoinj.core.Address;
import co.rsk.bitcoinj.core.BtcECKey;
import co.rsk.bitcoinj.core.BtcTransaction;
import co.rsk.bitcoinj.core.Coin;
import co.rsk.bitcoinj.core.NetworkParameters;
import co.rsk.bitcoinj.core.Sha256Hash;
import co.rsk.bitcoinj.crypto.TransactionSignature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.spongycastle.util.encoders.Hex;

public class NewRedeemScriptTest {
@Test
public void spendFromP2shP2wshAddressWithNewRedeem() {
NetworkParameters networkParameters = NetworkParameters.fromID(NetworkParameters.ID_TESTNET);

// Created with GenNodeKeyId using seed 'fed1'
byte[] publicKeyBytes = Hex.decode("043267e382e076cbaa199d49ea7362535f95b135de181caf66b391f541bf39ab0e75b8577faac2183782cb0d76820cf9f356831d216e99d886f8a6bc47fe696939");
BtcECKey btcKey1 = BtcECKey.fromPublicOnly(publicKeyBytes);
BtcECKey fed1PrivKey = BtcECKey.fromPrivate(Hex.decode("529822842595a3a6b3b3e51e9cffa0db66452599f7beec542382a02b1e42be4b"));

// Created with GenNodeKeyId using seed 'fed3', used for fed2 to keep keys sorted
publicKeyBytes = Hex.decode("0443e106d90183e2eef7d5cb7538a634439bf1301d731787c6736922ff19e750ed39e74a76731fed620aeedbcd77e4de403fc4148efd3b5dbfc6cef550aa63c377");
BtcECKey btcKey2 = BtcECKey.fromPublicOnly(publicKeyBytes);
BtcECKey fed2PrivKey = BtcECKey.fromPrivate(Hex.decode("b2889610e66cd3f7de37c81c20c786b576349b80b3f844f8409e3a29d95c0c7c"));

// Created with GenNodeKeyId using seed 'fed2', used for fed3 to keep keys sorted
publicKeyBytes = Hex.decode("04bd5b51b1c5d799da190285c8078a2712b8e5dc6f73c799751e6256bb89a4bd04c6444b00289fc76ee853fcfa52b3083d66c42e84f8640f53a4cdf575e4d4a399");
BtcECKey btcKey3 = BtcECKey.fromPublicOnly(publicKeyBytes);
BtcECKey fed3PrivKey = BtcECKey.fromPrivate(Hex.decode("fa013890aa14dd269a0ca16003cabde1688021358b662d17b1e8c555f5cccc6e"));

List<BtcECKey> keys = Arrays.asList(btcKey1, btcKey2, btcKey3);
List<BtcECKey> privateKeys = Arrays.asList(fed1PrivKey, fed2PrivKey, fed3PrivKey);

Script redeemScript = new ScriptBuilder().createNewRedeemScript(keys.size() / 2 + 1, keys);

Script p2shOutputScript = ScriptBuilder.createP2SHOutputScript(redeemScript);
Address legacyAddress = Address.fromP2SHScript(
networkParameters,
p2shOutputScript
);
System.out.println(legacyAddress);


Sha256Hash fundTxHash = Sha256Hash.wrap("7728d0ad5126afe126fe39243da57a42d66effdadda0f5825fafbd51ac1bb7ef");
int outputIndex = 0;
Address destinationAddress = Address.fromBase58(networkParameters, "msgc5Gtz2L9MVhXPDrFRCYPa16QgoZ2EjP"); // testnet
Coin value = Coin.valueOf(10_000);
Coin fee = Coin.valueOf(1_000);

BtcTransaction spendTx = new BtcTransaction(networkParameters);
spendTx.addInput(fundTxHash, outputIndex, new Script(new byte[]{}));
spendTx.addOutput(value.minus(fee), destinationAddress);
spendTx.setVersion(2);

// Create signatures
int inputIndex = 0;
Sha256Hash sigHash = spendTx.hashForSignature(
inputIndex,
redeemScript,
BtcTransaction.SigHash.ALL,
false
);

int requiredSignatures = privateKeys.size() / 2 + 1;
List<TransactionSignature> signatures = new ArrayList<>();

for (int i = 0; i < requiredSignatures; i++) {
BtcECKey keyToSign = privateKeys.get(i);
BtcECKey.ECDSASignature signature = keyToSign.sign(sigHash);
TransactionSignature txSignature = new TransactionSignature(
signature,
BtcTransaction.SigHash.ALL,
false
);
signatures.add(txSignature);
}

ScriptBuilder scriptBuilder = new ScriptBuilder();
Script scriptSig = scriptBuilder
.data(TransactionSignature.dummy().encodeToBitcoin())
.data(signatures.get(1).encodeToBitcoin())
.data(signatures.get(0).encodeToBitcoin())
.data(redeemScript.getProgram())
.build();

spendTx.getInput(0).setScriptSig(scriptSig);
scriptSig.correctlySpends(spendTx, 0, p2shOutputScript, ALL_VERIFY_FLAGS);

// Uncomment to print the raw tx in console and broadcast https://blockstream.info/testnet/tx/push
System.out.println(Hex.toHexString(spendTx.bitcoinSerialize()));
}
}