Skip to content

Commit

Permalink
Merge pull request #2727 from rsksmart/flyover-rs-parser
Browse files Browse the repository at this point in the history
Create a flyover redeem script builder
  • Loading branch information
josedahlquist authored Sep 17, 2024
2 parents 78a6dc0 + 08b28eb commit 0994382
Show file tree
Hide file tree
Showing 16 changed files with 578 additions and 263 deletions.
7 changes: 2 additions & 5 deletions rskj-core/src/main/java/co/rsk/peg/bitcoin/BitcoinUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@ public static Optional<Sha256Hash> getFirstInputSigHash(BtcTransaction btcTx){
}
TransactionInput txInput = btcTx.getInput(FIRST_INPUT_INDEX);
Optional<Script> redeemScript = extractRedeemScriptFromInput(txInput);
if (!redeemScript.isPresent()) {
return Optional.empty();
}

return Optional.of(btcTx.hashForSignature(
return redeemScript.map(script -> btcTx.hashForSignature(
FIRST_INPUT_INDEX,
redeemScript.get(),
script,
BtcTransaction.SigHash.ALL,
false
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import co.rsk.bitcoinj.core.BtcECKey;
import co.rsk.bitcoinj.script.Script;

import java.util.List;

public interface ErpRedeemScriptBuilder {

Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue);
Script of(
List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue
);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
package co.rsk.peg.bitcoin;

import static co.rsk.peg.bitcoin.RedeemScriptCreationException.Reason.INVALID_CSV_VALUE;
import static co.rsk.peg.bitcoin.RedeemScriptCreationException.Reason.INVALID_INTERNAL_REDEEM_SCRIPTS;

import co.rsk.bitcoinj.script.Script;
import co.rsk.bitcoinj.script.ScriptChunk;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static co.rsk.peg.bitcoin.RedeemScriptCreationException.Reason.INVALID_CSV_VALUE;
import static co.rsk.peg.bitcoin.RedeemScriptCreationException.Reason.INVALID_INTERNAL_REDEEM_SCRIPTS;

public class ErpRedeemScriptBuilderUtils {
private static final Logger logger = LoggerFactory.getLogger(ErpRedeemScriptBuilderUtils.class);
public static final long MAX_CSV_VALUE = 65535L; // 2^16 - 1, since bitcoin will interpret up to 16 bits as the CSV value
public static final long MAX_CSV_VALUE = 65_535L; // 2^16 - 1, since bitcoin will interpret up to 16 bits as the CSV value

private ErpRedeemScriptBuilderUtils() {
}
private ErpRedeemScriptBuilderUtils() {}

public static List<ScriptChunk> removeOpCheckMultisig(Script redeemScript) {
return redeemScript.getChunks().subList(0, redeemScript.getChunks().size() - 1);
Expand All @@ -27,7 +25,6 @@ public static void validateRedeemScriptValues(
Long csvValue
) {
if (!defaultFederationRedeemScript.isSentToMultiSig() || !erpFederationRedeemScript.isSentToMultiSig()) {

String message = "Provided redeem scripts have an invalid structure, not standard";
logger.debug(
"[validateRedeemScriptValues] {}. Default script {}. Emergency script {}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package co.rsk.peg.bitcoin;

import co.rsk.bitcoinj.script.Script;
import co.rsk.crypto.Keccak256;

public interface FlyoverRedeemScriptBuilder {
Script of(Keccak256 flyoverDerivationHash, Script redeemScript);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package co.rsk.peg.bitcoin;

import static co.rsk.peg.bitcoin.RedeemScriptCreationException.Reason.INVALID_FLYOVER_DERIVATION_HASH;
import static java.util.Objects.isNull;

import co.rsk.bitcoinj.script.Script;
import co.rsk.bitcoinj.script.ScriptBuilder;
import co.rsk.bitcoinj.script.ScriptOpCodes;
import co.rsk.crypto.Keccak256;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FlyoverRedeemScriptBuilderImpl implements FlyoverRedeemScriptBuilder {
private static final Logger logger = LoggerFactory.getLogger(FlyoverRedeemScriptBuilderImpl.class);

private FlyoverRedeemScriptBuilderImpl() {}

public static FlyoverRedeemScriptBuilderImpl builder() {
return new FlyoverRedeemScriptBuilderImpl();
}

@Override
public Script of(Keccak256 flyoverDerivationHash, Script redeemScript) {
validateFlyoverDerivationHash(flyoverDerivationHash);

ScriptBuilder scriptBuilder = new ScriptBuilder();
byte[] flyoverDerivationHashSerialized = flyoverDerivationHash.getBytes();

return scriptBuilder
.data(flyoverDerivationHashSerialized)
.op(ScriptOpCodes.OP_DROP)
.addChunks(redeemScript.getChunks())
.build();
}

private void validateFlyoverDerivationHash(Keccak256 flyoverDerivationHash) {
if (isNull(flyoverDerivationHash) || flyoverDerivationHash.equals(Keccak256.ZERO_HASH)) {
String message = String.format("Provided flyover derivation hash %s is invalid.", flyoverDerivationHash);
logger.warn("[validateFlyoverDerivationHash] {}", message);
throw new RedeemScriptCreationException(message, INVALID_FLYOVER_DERIVATION_HASH);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
package co.rsk.peg.bitcoin;

import static co.rsk.peg.bitcoin.ErpRedeemScriptBuilderUtils.removeOpCheckMultisig;

import co.rsk.bitcoinj.core.BtcECKey;
import co.rsk.bitcoinj.core.Utils;
import co.rsk.bitcoinj.script.*;
import co.rsk.bitcoinj.script.Script;
import co.rsk.bitcoinj.script.ScriptBuilder;
import co.rsk.bitcoinj.script.ScriptOpCodes;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static co.rsk.peg.bitcoin.ErpRedeemScriptBuilderUtils.removeOpCheckMultisig;

public class NonStandardErpRedeemScriptBuilder implements ErpRedeemScriptBuilder {
private static final Logger logger = LoggerFactory.getLogger(NonStandardErpRedeemScriptBuilder.class);

@Override
public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue) {
private NonStandardErpRedeemScriptBuilder() {}

public static NonStandardErpRedeemScriptBuilder builder() {
return new NonStandardErpRedeemScriptBuilder();
}

@Override
public Script of(
List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue
) {
Script defaultRedeemScript = ScriptBuilder.createRedeemScript(defaultThreshold, defaultPublicKeys);
Script emergencyRedeemScript = ScriptBuilder.createRedeemScript(emergencyThreshold, emergencyPublicKeys);

Expand All @@ -33,13 +41,13 @@ public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
ScriptValidations.validateScriptSize(redeemScript);

return redeemScript;

}

private Script createRedeemScriptFromScripts(Script defaultRedeemScript,
Script emergencyRedeemScript,
byte[] serializedCsvValue) {

private Script createRedeemScriptFromScripts(
Script defaultRedeemScript,
Script emergencyRedeemScript,
byte[] serializedCsvValue
) {
ScriptBuilder scriptBuilder = new ScriptBuilder();
return scriptBuilder
.op(ScriptOpCodes.OP_NOTIF)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ private NonStandardErpRedeemScriptBuilderFactory() {}

public static ErpRedeemScriptBuilder getNonStandardErpRedeemScriptBuilder(
ActivationConfig.ForBlock activations,
NetworkParameters networkParameters) {

NetworkParameters networkParameters
) {
if (networkIsTestnet(networkParameters) && !activations.isActive(ConsensusRule.RSKIP284)) {
return new NonStandardErpRedeemScriptBuilderHardcoded();
return NonStandardErpRedeemScriptBuilderHardcoded.builder();
}
if (!activations.isActive(ConsensusRule.RSKIP293)) {
return new NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE();
return NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE.builder();
}
return new NonStandardErpRedeemScriptBuilder();

return NonStandardErpRedeemScriptBuilder.builder();
}

private static boolean networkIsTestnet(NetworkParameters networkParameters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@

import co.rsk.bitcoinj.core.BtcECKey;
import co.rsk.bitcoinj.script.Script;
import java.util.List;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class NonStandardErpRedeemScriptBuilderHardcoded implements ErpRedeemScriptBuilder {
private static final Logger logger = LoggerFactory.getLogger(NonStandardErpRedeemScriptBuilderHardcoded.class);
private static final byte[] LEGACY_ERP_TESTNET_REDEEM_SCRIPT_BYTES = Hex.decode("6453210208f40073a9e43b3e9103acec79767a6de9b0409749884e989960fee578012fce210225e892391625854128c5c4ea4340de0c2a70570f33db53426fc9c746597a03f42102afc230c2d355b1a577682b07bc2646041b5d0177af0f98395a46018da699b6da210344a3c38cd59afcba3edcebe143e025574594b001700dec41e59409bdbd0f2a0921039a060badbeb24bee49eb2063f616c0f0f0765d4ca646b20a88ce828f259fcdb955670300cd50b27552210216c23b2ea8e4f11c3f9e22711addb1d16a93964796913830856b568cc3ea21d3210275562901dd8faae20de0a4166362a4f82188db77dbed4ca887422ea1ec185f1421034db69f2112f4fb1bb6141bf6e2bd6631f0484d0bd95b16767902c9fe219d4a6f5368ae");

@Override
public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue) {
private NonStandardErpRedeemScriptBuilderHardcoded() {}

public static NonStandardErpRedeemScriptBuilderHardcoded builder() {
return new NonStandardErpRedeemScriptBuilderHardcoded();
}

@Override
public Script of(
List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue
) {
logger.debug("[getRedeemScript] Returning hardcoded redeem script");
return new Script(LEGACY_ERP_TESTNET_REDEEM_SCRIPT_BYTES);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
package co.rsk.peg.bitcoin;

import static co.rsk.peg.bitcoin.ErpRedeemScriptBuilderUtils.removeOpCheckMultisig;

import co.rsk.bitcoinj.core.BtcECKey;
import co.rsk.bitcoinj.core.Utils;
import co.rsk.bitcoinj.script.Script;
import co.rsk.bitcoinj.script.ScriptBuilder;
import co.rsk.bitcoinj.script.ScriptOpCodes;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

import static co.rsk.peg.bitcoin.ErpRedeemScriptBuilderUtils.removeOpCheckMultisig;

public class NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE implements ErpRedeemScriptBuilder {
private static final Logger logger = LoggerFactory.getLogger(NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE.class);
private static final int CSV_BYTES_NEEDED_LENGTH = 2;

@Override
public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue) {
private NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE() {}

public static NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE builder() {
return new NonStandardErpRedeemScriptBuilderWithCsvUnsignedBE();
}

@Override
public Script of(
List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue
) {
Script defaultRedeemScript = ScriptBuilder.createRedeemScript(defaultThreshold, defaultPublicKeys);
Script emergencyRedeemScript = ScriptBuilder.createRedeemScript(emergencyThreshold, emergencyPublicKeys);

Expand All @@ -36,13 +42,13 @@ public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
ScriptValidations.validateScriptSize(redeemScript);

return redeemScript;

}

private Script createRedeemScriptFromScripts(Script defaultRedeemScript,
Script emergencyRedeemScript,
byte[] serializedCsvValue) {

private Script createRedeemScriptFromScripts(
Script defaultRedeemScript,
Script emergencyRedeemScript,
byte[] serializedCsvValue
) {
ScriptBuilder scriptBuilder = new ScriptBuilder();
return scriptBuilder
.op(ScriptOpCodes.OP_NOTIF)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,27 @@
import co.rsk.bitcoinj.script.Script;
import co.rsk.bitcoinj.script.ScriptBuilder;
import co.rsk.bitcoinj.script.ScriptOpCodes;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class P2shErpRedeemScriptBuilder implements ErpRedeemScriptBuilder{
private static final Logger logger = LoggerFactory.getLogger(P2shErpRedeemScriptBuilder.class);

@Override
public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue) {
private P2shErpRedeemScriptBuilder() {}

public static P2shErpRedeemScriptBuilder builder() {
return new P2shErpRedeemScriptBuilder();
}

@Override
public Script of(
List<BtcECKey> defaultPublicKeys,
int defaultThreshold,
List<BtcECKey> emergencyPublicKeys,
int emergencyThreshold,
long csvValue
) {
Script defaultRedeemScript = ScriptBuilder.createRedeemScript(defaultThreshold, defaultPublicKeys);
Script emergencyRedeemScript = ScriptBuilder.createRedeemScript(emergencyThreshold, emergencyPublicKeys);

Expand All @@ -34,10 +40,12 @@ public Script createRedeemScriptFromKeys(List<BtcECKey> defaultPublicKeys,

return redeemScript;
}
private Script createRedeemScriptFromScripts(Script defaultRedeemScript,
Script emergencyRedeemScript,
byte[] serializedCsvValue) {

private Script createRedeemScriptFromScripts(
Script defaultRedeemScript,
Script emergencyRedeemScript,
byte[] serializedCsvValue
) {
ScriptBuilder scriptBuilder = new ScriptBuilder();

return scriptBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ public class RedeemScriptCreationException extends RuntimeException {

public enum Reason {
INVALID_INTERNAL_REDEEM_SCRIPTS,
INVALID_CSV_VALUE
INVALID_CSV_VALUE,
INVALID_FLYOVER_DERIVATION_HASH
}

public RedeemScriptCreationException(String s, Reason reason) {
Expand Down
Loading

0 comments on commit 0994382

Please sign in to comment.