Skip to content

Commit

Permalink
Correct tests to create realistic transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
julia-zack committed Sep 11, 2024
1 parent d18086a commit 0b60713
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 39 deletions.
2 changes: 1 addition & 1 deletion rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ private void processSvpFundTransactionHashSigned(BtcTransaction transaction) {
Sha256Hash fundTransactionHashUnsigned = fundTransactionHashUnsignedOpt.get();

BtcTransaction transactionCopy = new BtcTransaction(networkParameters, transaction.bitcoinSerialize()); // this is needed to not remove signatures from the actual tx
BitcoinUtils.removeSignaturesFromTransactionWithInputsWithP2shMultiSigInputScript(transactionCopy);
BitcoinUtils.removeSignaturesFromTransactionWithP2shMultiSigInputs(transactionCopy);
if (transactionCopy.getHash().equals(fundTransactionHashUnsigned)) {
provider.setSvpFundTxHashSigned(transaction.getHash());
provider.setSvpFundTxHashUnsigned(null);
Expand Down
116 changes: 78 additions & 38 deletions rskj-core/src/test/java/co/rsk/peg/BridgeSupportTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,8 @@ void processSvpFundTransactionWithoutSignatures_whenThereAreNoEnoughUTXOs_throws
@Test
void processSvpFundTransactionWithoutSignatures_createsExpectedTransactionAndSavesTheHashInStorageEntryAndPerformsPegoutActions() throws Exception {
// act
processAndSaveSvpFundTransactionUnsigned();
bridgeSupport.processSvpFundTransactionUnsigned(rskTx);
bridgeSupport.save(); // to save the tx sig hash

// assert
assertSvpFundTxHashUnsignedWasSavedInStorage();
Expand Down Expand Up @@ -748,15 +749,14 @@ private Optional<TransactionOutput> searchForOutput(List<TransactionOutput> tran
@Test
void registerBtcTransaction_forSvpFundTransaction_whenProposedFederationDoesNotExist_shouldRegisterTransactionButNotUpdateSvpFundTransactionValues() throws Exception {
// arrange
processAndSaveSvpFundTransactionUnsigned();
BtcTransaction svpFundTransaction = getSvpFundTransactionFromPegoutsMap(bridgeStorageProvider.getPegoutsWaitingForConfirmations());
randomSignTransactionInputs(svpFundTransaction, activeFederation.getNumberOfSignaturesRequired());
BtcTransaction svpFundTransaction = createAndSaveSvpFundTransactionUnsigned();
signInputs(svpFundTransaction); // a transaction trying to be registered should be signed

// recreate a valid chain that has the tx, so it passes the previous checks in registerBtcTransaction
PartialMerkleTree pmtWithTransactions = createValidPmtWithTransactions(Collections.singletonList(svpFundTransaction.getHash()), btcMainnetParams);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(Collections.singletonList(svpFundTransaction.getHash()), btcMainnetParams);
int btcBlockWithPmtHeight = bridgeMainNetConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeMainNetConstants.getPegoutTxIndexGracePeriodInBtcBlocks(); // we want pegout tx index to be activated
int chainHeight = btcBlockWithPmtHeight + bridgeMainNetConstants.getBtc2RskMinimumAcceptableConfirmations();
recreateChainAtHeightWithStoredBlockWithPmtAtHeight(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
bridgeStorageProvider.save();
// recreate bridge support to have this chain
bridgeSupport = bridgeSupportBuilder
Expand Down Expand Up @@ -784,6 +784,9 @@ void registerBtcTransaction_forSvpFundTransaction_whenProposedFederationDoesNotE
@Test
void registerBtcTransaction_forSvpFundTransaction_whenValidationPeriodEnded_shouldRegisterTransactionButNotUpdateSvpFundTransactionValues() throws Exception {
// arrange
BtcTransaction svpFundTransaction = createAndSaveSvpFundTransactionUnsigned();
signInputs(svpFundTransaction); // a transaction trying to be registered should be signed

// make rsk execution block to be after validation period ended
long validationPeriodEndBlock = proposedFederation.getCreationBlockNumber()
+ bridgeMainNetConstants.getFederationConstants().getValidationPeriodDurationInBlocks();
Expand All @@ -794,15 +797,11 @@ void registerBtcTransaction_forSvpFundTransaction_whenValidationPeriodEnded_shou
.build();
rskExecutionBlock = Block.createBlockFromHeader(blockHeader, true);

processAndSaveSvpFundTransactionUnsigned();
BtcTransaction svpFundTransaction = getSvpFundTransactionFromPegoutsMap(bridgeStorageProvider.getPegoutsWaitingForConfirmations());
randomSignTransactionInputs(svpFundTransaction, activeFederation.getNumberOfSignaturesRequired());

// recreate a valid chain that has the tx, so it passes the previous checks in registerBtcTransaction
PartialMerkleTree pmtWithTransactions = createValidPmtWithTransactions(Collections.singletonList(svpFundTransaction.getHash()), btcMainnetParams);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(Collections.singletonList(svpFundTransaction.getHash()), btcMainnetParams);
int btcBlockWithPmtHeight = bridgeMainNetConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeMainNetConstants.getPegoutTxIndexGracePeriodInBtcBlocks(); // we want pegout tx index to be activated
int chainHeight = btcBlockWithPmtHeight + bridgeMainNetConstants.getBtc2RskMinimumAcceptableConfirmations();
recreateChainAtHeightWithStoredBlockWithPmtAtHeight(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
bridgeStorageProvider.save();
// recreate bridge support to have this chain
bridgeSupport = bridgeSupportBuilder
Expand All @@ -828,26 +827,15 @@ void registerBtcTransaction_forSvpFundTransaction_whenValidationPeriodEnded_shou
@Test
void registerBtcTransaction_forNormalPegout_whenSvpPeriodIsOngoing_shouldRegisterTransactionButNotUpdateSvpFundTransactionValues() throws Exception {
// Arrange
processAndSaveSvpFundTransactionUnsigned();

BtcTransaction pegoutTransaction = new BtcTransaction(btcMainnetParams);
// add random input. Just using a federation because it is easier to build.
Federation standardMultisigFederation = new StandardMultiSigFederationBuilder().build();
Script standardMultisigP2SHScript = standardMultisigFederation.getP2SHScript();
pegoutTransaction.addInput(BitcoinTestUtils.createHash(1), 0, standardMultisigP2SHScript.createEmptyInputScript(null, standardMultisigFederation.getRedeemScript()));
randomSignTransactionInputs(pegoutTransaction, activeFederation.getNumberOfSignaturesRequired());
// add output to the active fed
Script activeFederationP2SHScript = activeFederation.getP2SHScript();
pegoutTransaction.addOutput(Coin.COIN.multiply(10), activeFederationP2SHScript);
// save sigHash in pegout index
Optional<Sha256Hash> inputSigHash = BitcoinUtils.getFirstInputSigHash(pegoutTransaction);
bridgeStorageProvider.setPegoutTxSigHash(inputSigHash.get());
createAndSaveSvpFundTransactionUnsigned();
BtcTransaction pegout = createAndSavePegout();
signInputs(pegout); // a transaction trying to be registered should be signed

// recreate a valid chain that has the tx, so it passes the previous checks in registerBtcTransaction
PartialMerkleTree pmtWithTransactions = createValidPmtWithTransactions(Collections.singletonList(pegoutTransaction.getHash()), btcMainnetParams);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(Collections.singletonList(pegout.getHash()), btcMainnetParams);
int btcBlockWithPmtHeight = bridgeMainNetConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeMainNetConstants.getPegoutTxIndexGracePeriodInBtcBlocks(); // we want pegout tx index to be activated
int chainHeight = btcBlockWithPmtHeight + bridgeMainNetConstants.getBtc2RskMinimumAcceptableConfirmations();
recreateChainAtHeightWithStoredBlockWithPmtAtHeight(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
bridgeStorageProvider.save();
// recreate bridge support to have this chain
bridgeSupport = bridgeSupportBuilder
Expand All @@ -863,7 +851,7 @@ void registerBtcTransaction_forNormalPegout_whenSvpPeriodIsOngoing_shouldRegiste

// Act
int activeFederationUtxosSizeBeforeRegisteringTx = federationSupport.getActiveFederationBtcUTXOs().size();
bridgeSupport.registerBtcTransaction(rskTx, pegoutTransaction.bitcoinSerialize(), btcBlockWithPmtHeight, pmtWithTransactions.bitcoinSerialize());
bridgeSupport.registerBtcTransaction(rskTx, pegout.bitcoinSerialize(), btcBlockWithPmtHeight, pmtWithTransactions.bitcoinSerialize());
bridgeStorageProvider.save();

// assert
Expand All @@ -879,15 +867,14 @@ private void assertTransactionWasRegisteredButSignedHashWasNotSavedAndUnsignedHa
@Test
void registerBtcTransaction_forSvpFundTransaction_whenSvpPeriodIsOngoing_shouldRegisterTransactionAndUpdateSvpFundTransactionValues() throws Exception {
// Arrange
processAndSaveSvpFundTransactionUnsigned();
BtcTransaction svpFundTransaction = getSvpFundTransactionFromPegoutsMap(bridgeStorageProvider.getPegoutsWaitingForConfirmations());
randomSignTransactionInputs(svpFundTransaction, activeFederation.getNumberOfSignaturesRequired());
BtcTransaction svpFundTransaction = createAndSaveSvpFundTransactionUnsigned();
signInputs(svpFundTransaction); // a transaction trying to be registered should be signed

// recreate a valid chain that has the tx, so it passes the previous checks in registerBtcTransaction
PartialMerkleTree pmtWithTransactions = createValidPmtWithTransactions(Collections.singletonList(svpFundTransaction.getHash()), btcMainnetParams);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(Collections.singletonList(svpFundTransaction.getHash()), btcMainnetParams);
int btcBlockWithPmtHeight = bridgeMainNetConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeMainNetConstants.getPegoutTxIndexGracePeriodInBtcBlocks(); // we want pegout tx index to be activated
int chainHeight = btcBlockWithPmtHeight + bridgeMainNetConstants.getBtc2RskMinimumAcceptableConfirmations();
recreateChainAtHeightWithStoredBlockWithPmtAtHeight(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcMainnetParams);
bridgeStorageProvider.save();
// recreate bridge support to have this chain
bridgeSupport = bridgeSupportBuilder
Expand Down Expand Up @@ -921,9 +908,62 @@ void registerBtcTransaction_forSvpFundTransaction_whenSvpPeriodIsOngoing_shouldR
assertFalse(bridgeStorageProvider.getSvpFundTxHashUnsigned().isPresent());
}

private void processAndSaveSvpFundTransactionUnsigned() throws InsufficientMoneyException, IOException {
bridgeSupport.processSvpFundTransactionUnsigned(rskTx);
bridgeSupport.save(); // to save the tx sig hash
private BtcTransaction createAndSaveSvpFundTransactionUnsigned() {
BtcTransaction svpFundTransaction = new BtcTransaction(btcMainnetParams);
addInputs(svpFundTransaction, 1);
addOutputChange(svpFundTransaction);
savePegoutIndex(svpFundTransaction);
saveSvpFundTransactionHashUnsigned(svpFundTransaction.getHash());

return svpFundTransaction;
}

private BtcTransaction createAndSavePegout() {
BtcTransaction pegout = new BtcTransaction(btcMainnetParams);
addInputs(pegout, 2);
addOutputChange(pegout);
savePegoutIndex(pegout);

return pegout;
}

private void addInputs(BtcTransaction transaction, int inputsSize) {
// we need to add inputs that we can actually sign,
// that meaning we need to know the private keys
Federation federation = new P2shErpFederationBuilder().build();
Script scriptSig = federation.getP2SHScript().createEmptyInputScript(null, federation.getRedeemScript());

for (int i = 0; i < inputsSize; i++){
Sha256Hash parentTxHash = BitcoinTestUtils.createHash(1);
// using i as the outputIndex to avoid duplicated outpoint exception
transaction.addInput(parentTxHash, i, scriptSig);
}
}

private void signInputs(BtcTransaction transaction) {
List<BtcECKey> keysToSign =
BitcoinTestUtils.getBtcEcKeysFromSeeds(new String[]{"member01", "member02", "member03", "member04", "member05"}, true);
List<TransactionInput> inputs = transaction.getInputs();
for (TransactionInput input : inputs) {
BitcoinTestUtils.signTransactionInputFromP2shMultiSig(transaction, inputs.indexOf(input), keysToSign);
}
}

private void addOutputChange(BtcTransaction transaction) {
// add output to the active fed
Script activeFederationP2SHScript = activeFederation.getP2SHScript();
transaction.addOutput(Coin.COIN.multiply(10), activeFederationP2SHScript);
}

private void savePegoutIndex(BtcTransaction pegout) {
// save sigHash in pegout index
Optional<Sha256Hash> inputSigHash = BitcoinUtils.getFirstInputSigHash(pegout);
bridgeStorageProvider.setPegoutTxSigHash(inputSigHash.get());
}

private void saveSvpFundTransactionHashUnsigned(Sha256Hash svpFundTransactionHashUnsigned) {
bridgeStorageProvider.setSvpFundTxHashUnsigned(svpFundTransactionHashUnsigned);
bridgeSupport.save();
}
}

Expand Down Expand Up @@ -1000,7 +1040,7 @@ private void randomSignTransactionInputs(BtcTransaction transaction, int amountO
List<TransactionInput> inputs = transaction.getInputs();
for (TransactionInput input : inputs) {
int inputIndex = inputs.indexOf(input);
BitcoinTestUtils.signTransactionInputFromMultiSigWithKeys(transaction, inputIndex, keysToSign);
BitcoinTestUtils.signTransactionInputFromP2shMultiSig(transaction, inputIndex, keysToSign);
}
}

Expand Down

0 comments on commit 0b60713

Please sign in to comment.