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

Create a flyover redeem script builder #2727

Merged
merged 6 commits into from
Sep 17, 2024
Merged
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
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why merge it into the master branch instead of the integration branch, wip/fed-scripts-refactors-integration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes it easier to integrate all the changes we are working on in different branches

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
Loading