Skip to content

Commit

Permalink
Merge pull request #2989 from rsksmart/vovchyk/merge-rc700-master
Browse files Browse the repository at this point in the history
Merge LOVELL-7.0.0-rc -> master
  • Loading branch information
Vovchyk authored Feb 13, 2025
2 parents 8e6729d + 292fa0b commit 607ef6e
Show file tree
Hide file tree
Showing 8 changed files with 1,262 additions and 1,816 deletions.
40 changes: 30 additions & 10 deletions rskj-core/src/main/java/co/rsk/peg/PegUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,20 @@ private static boolean isTheSvpFundTransaction(
BtcTransaction transaction
) {
return provider.getSvpFundTxHashUnsigned()
.filter(svpFundTransactionHashUnsigned ->
getMultiSigTransactionHashWithoutSignatures(networkParameters, transaction).equals(svpFundTransactionHashUnsigned)
)
.isPresent();
.map(svpFundTxHashUnsigned -> {
try {
Sha256Hash txHashWithoutSignatures = getMultiSigTransactionHashWithoutSignatures(networkParameters, transaction);
return svpFundTxHashUnsigned.equals(txHashWithoutSignatures);
} catch (IllegalArgumentException e) {
logger.trace(
"[isTheSvpFundTransaction] Btc tx {} either has witness or non p2sh-legacy-multisig inputs, so we'll assume is not the fund tx",
transaction.getHash(),
e
);
return false;
}
})
.orElse(false);
}

private static boolean isTheSvpSpendTransaction(
Expand All @@ -163,10 +173,20 @@ private static boolean isTheSvpSpendTransaction(
BtcTransaction transaction
) {
return provider.getSvpSpendTxHashUnsigned()
.filter(svpSpendTransactionHashUnsigned ->
getMultiSigTransactionHashWithoutSignatures(networkParameters, transaction).equals(svpSpendTransactionHashUnsigned)
)
.isPresent();
.map(svpSpendTxHashUnsigned -> {
try {
Sha256Hash txHashWithoutSignatures = getMultiSigTransactionHashWithoutSignatures(networkParameters, transaction);
return svpSpendTxHashUnsigned.equals(txHashWithoutSignatures);
} catch (IllegalArgumentException e) {
logger.trace(
"[isTheSvpSpendTransaction] Btc tx {} either has witness or non p2sh-legacy-multisig inputs, so we'll assume is not the spend tx",
transaction.getHash(),
e
);
return false;
}
})
.orElse(false);
}

static PeginEvaluationResult evaluatePegin(
Expand All @@ -176,11 +196,11 @@ static PeginEvaluationResult evaluatePegin(
Wallet fedWallet,
ActivationConfig.ForBlock activations
) {
if(!activations.isActive(ConsensusRule.RSKIP379)) {
if (!activations.isActive(ConsensusRule.RSKIP379)) {
throw new IllegalStateException("Can't call this method before RSKIP379 activation");
}

if(!allUTXOsToFedAreAboveMinimumPeginValue(btcTx, fedWallet, minimumPeginTxValue, activations)) {
if (!allUTXOsToFedAreAboveMinimumPeginValue(btcTx, fedWallet, minimumPeginTxValue, activations)) {
logger.debug("[evaluatePegin] Peg-in contains at least one utxo below the minimum value");
return new PeginEvaluationResult(PeginProcessAction.NO_REFUND, INVALID_AMOUNT);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static Sha256Hash getMultiSigTransactionHashWithoutSignatures(NetworkPara
return transaction.getHash();
}

private static void removeSignaturesFromTransactionWithP2shMultiSigInputs(BtcTransaction transaction) {
public static void removeSignaturesFromTransactionWithP2shMultiSigInputs(BtcTransaction transaction) {
if (transaction.hasWitness()) {
String message = "Removing signatures from SegWit transactions is not allowed.";
logger.error("[removeSignaturesFromTransactionWithP2shMultiSigInputs] {}", message);
Expand Down
2 changes: 1 addition & 1 deletion rskj-core/src/main/resources/config/main.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ blockchain.config {
fingerroot500 = 5468000,
arrowhead600 = 6223700,
arrowhead631 = 6549300,
lovell700 = -1
lovell700 = 7338024
}
}

Expand Down
2 changes: 1 addition & 1 deletion rskj-core/src/main/resources/config/testnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ blockchain.config {
fingerroot500 = 4015800,
arrowhead600 = 4927100,
arrowhead631 = -1,
lovell700 = -1
lovell700 = 6110487
},
consensusRules = {
rskip97 = -1, # disable orchid difficulty drop
Expand Down
336 changes: 332 additions & 4 deletions rskj-core/src/test/java/co/rsk/peg/BridgeSupportSvpTest.java

Large diffs are not rendered by default.

94 changes: 93 additions & 1 deletion rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static co.rsk.bitcoinj.script.ScriptOpCodes.OP_NOT;
import static co.rsk.peg.bitcoin.BitcoinUtils.*;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.junit.jupiter.api.Assertions.*;

import co.rsk.bitcoinj.core.*;
Expand Down Expand Up @@ -434,6 +435,97 @@ void getTransactionHashWithoutSignatures_forRealPegout() {
assertEquals(hashFromReleaseRequestedEvent, btcTxHashWithoutSigs);
}

@Test
void removeSignaturesFromTransactionWithP2shMultiSigInputs_whenTransactionIsLegacyAndInputsHaveP2shMultiSigInputScript_shouldReturnExpectedTx() {
// arrange
Federation federation = P2shErpFederationBuilder.builder().build();
Script federationRedeemScript = federation.getRedeemScript();
Script scriptSig = federation.getP2SHScript().createEmptyInputScript(null, federationRedeemScript);

BtcTransaction transaction = new BtcTransaction(btcMainnetParams);
transaction.addInput(BitcoinTestUtils.createHash(1), 0, scriptSig);
transaction.addInput(BitcoinTestUtils.createHash(2), 0, scriptSig);
transaction.addOutput(Coin.COIN, destinationAddress);

BtcTransaction transactionBeforeSigning = new BtcTransaction(btcMainnetParams, transaction.bitcoinSerialize());
List<BtcECKey> keysToSign = BitcoinTestUtils.getBtcEcKeysFromSeeds(new String[]{
"member01",
"member02",
"member03",
"member04",
"member05"
}, true); // using private keys from federation declared above
List<TransactionInput> inputs = transaction.getInputs();
for (TransactionInput input : inputs) {
BitcoinTestUtils.signTransactionInputFromP2shMultiSig(
transaction,
inputs.indexOf(input),
keysToSign
);
}

// act
BitcoinUtils.removeSignaturesFromTransactionWithP2shMultiSigInputs(transaction);

// assert
assertEquals(transactionBeforeSigning, transaction);

for (TransactionInput input : transaction.getInputs()) {
List<ScriptChunk> scriptSigChunks = input.getScriptSig().getChunks();

// assert last script sig chunk is the redeem script
int redeemScriptChunkIndex = scriptSigChunks.size() - 1;
ScriptChunk redeemScriptChunk = scriptSigChunks.get(redeemScriptChunkIndex);
assertArrayEquals(federationRedeemScript.getProgram(), redeemScriptChunk.data);

// assert script sig does not have signatures
for (int i = 0; i < redeemScriptChunkIndex - 1; i++) {
int scriptSigChunkOpCode = scriptSigChunks.get(i).opcode;
assertEquals(ScriptOpCodes.OP_0, scriptSigChunkOpCode);
}
}
}

@Test
void removeSignaturesFromTransactionWithP2shMultiSigInputs_whenNotAllInputsHaveP2shMultiSigInputScript_shouldThrowIAE() {
// arrange
BtcTransaction transaction = new BtcTransaction(btcMainnetParams);

Federation federation = P2shErpFederationBuilder.builder().build();
Script scriptSig = federation.getP2SHScript().createEmptyInputScript(null, federation.getRedeemScript());
transaction.addInput(BitcoinTestUtils.createHash(1), 0, scriptSig);

// having an empty script sig means we cannot get the redeem script from it
Script emptyScriptSig = new Script(EMPTY_BYTE_ARRAY);
transaction.addInput(BitcoinTestUtils.createHash(2), 0, emptyScriptSig);

transaction.addOutput(Coin.COIN, destinationAddress);

List<BtcECKey> keysToSign = BitcoinTestUtils.getBtcEcKeysFromSeeds(new String[]{
"member01",
"member02",
"member03",
"member04",
"member05"
}, true); // using private keys from federation declared above
// we can only sign first input since the other input script sig will be empty
BitcoinTestUtils.signTransactionInputFromP2shMultiSig(transaction, 0, keysToSign);

// act & assert
assertThrows(IllegalArgumentException.class, () -> BitcoinUtils.removeSignaturesFromTransactionWithP2shMultiSigInputs(transaction));
}

@Test
void removeSignaturesFromTransactionWithP2shMultiSigInputs_whenTransactionHasWitness_shouldThrowIAE() {
// arrange
// data from tx https://mempool.space/testnet/tx/1744459aeaf7369aadc9fc40de9ab2bf575b14e35029b35a7ee4bbd3de65af7f
byte[] rawTx = Hex.decode("02000000000101d654c3944d02808dda61dc0269cb8211da06035dab73c0a332e21a5e27d5c6c6000000002322002054ff1b72e598122f983989e6df42dc75736650c2409a08a237d3c3a6220b1c87fdffffff010ca00700000000001976a914af0c6784340fca71dc85be74e48c06f7850be1da88ac040047304402201f35f5f48ac56ebac8b656279c33886f0776f2e99e445ad653fdb376a05f964002200dbba1180d880e690c3d5d80295d358a1e0e58eb7ade5788c6f5e41c36b75516014830450221008608b2760b3f376a25a745433e5e562800bacf342a4fd0266c0944ff036a4035022050ebec91481289c8266038fc9eaf63e9c9b133934ceb8752901d8bc35d0b7c4801695221027de2af71862e0c64bf0ec5a66e3abc3b01fc57877802e6a6a81f6ea1d35610072102d9c67fef9f8d0707cbcca195eb5f26c6a65da6ca2d6130645c434bb924063856210346f033b8652a17d319d3ecbbbf20fd2cd663a6548173b9419d8228eef095012e53ae57472500");
BtcTransaction transaction = new BtcTransaction(btcTestnetParams, rawTx);

// act & assert
assertThrows(IllegalArgumentException.class, () -> BitcoinUtils.removeSignaturesFromTransactionWithP2shMultiSigInputs(transaction));
}

@Test
void createBaseP2SHInputScriptThatSpendsFromRedeemScript_shouldCreateExpectedScriptSig() {
// arrange
Expand All @@ -458,7 +550,7 @@ void createBaseP2SHInputScriptThatSpendsFromRedeemScript_shouldCreateExpectedScr
assertArrayEquals(redeemScript.getProgram(), scriptSigChunks.get(redeemScriptChunkIndex).data); // last chunk should be the redeem script

for (ScriptChunk chunk : scriptSigChunks.subList(0, redeemScriptChunkIndex)) { // all the other chunks should be zero
assertEquals(0, chunk.opcode);
assertEquals(ScriptOpCodes.OP_0, chunk.opcode);
}
}

Expand Down
Loading

0 comments on commit 607ef6e

Please # to comment.