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

A PegIn including mulitples inputs should get the rsk receiver address from the first input #2936

Open
wants to merge 3 commits into
base: pegin-it-multipleOutpus-belowMinimum
Choose a base branch
from
Open
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
113 changes: 77 additions & 36 deletions rskj-core/src/test/java/co/rsk/peg/RegisterBtcTransactionIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@
import org.ethereum.vm.PrecompiledContracts;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.*;

class RegisterBtcTransactionIT {
public static final long RSK_EXECUTION_BLOCK_NUMBER = 1000L;
public static final long RSK_EXECUTION_BLOCK_TIMESTAMP = 10L;
private static int spendTxHashSeed = 0;
private static int outputIndex = 0;
private final BridgeConstants bridgeConstants = BridgeMainNetConstants.getInstance();
private final NetworkParameters btcNetworkParams = bridgeConstants.getBtcParams();
private final BridgeSupportBuilder bridgeSupportBuilder = BridgeSupportBuilder.builder();
Expand Down Expand Up @@ -104,7 +108,7 @@ void setUp() {
@Test
void registerBtcTransaction_forALegacyBtcTransaction_shouldRegisterTheNewUtxoAndTransferTheRbtcBalance() throws Exception {
// Arrange
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey);
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey, spendTxHashSeed, outputIndex);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
Expand All @@ -116,12 +120,10 @@ void registerBtcTransaction_forALegacyBtcTransaction_shouldRegisterTheNewUtxoAnd
bridgeSupport.save();

// Assert
Optional<Long> heightIfBtcTxHashIsAlreadyProcessed = bridgeStorageProvider.getHeightIfBtcTxhashIsAlreadyProcessed(btcTransaction.getHash());
assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent());
assertEquals(RSK_EXECUTION_BLOCK_NUMBER, heightIfBtcTxHashIsAlreadyProcessed.get());
assertItWasProcessed(btcTransaction);

int outputIndex = 0;
TransactionOutput output = btcTransaction.getOutput(outputIndex);
int newOutputIndex = 0;
TransactionOutput output = btcTransaction.getOutput(newOutputIndex);
List<UTXO> expectedFederationUtxos = List.of(utxoOf(btcTransaction, output));
assertEquals(expectedFederationUtxos, federationSupport.getActiveFederationBtcUTXOs());

Expand All @@ -135,7 +137,7 @@ void registerBtcTransaction_forALegacyBtcTransaction_shouldRegisterTheNewUtxoAnd
void registerBtcTransaction_forMultipleLegacyBtcTransaction_shouldRegisterTheNewUtxosAndTransferTheRbtcBalance() throws Exception {
// Arrange
short numberOfOutputs = 3;
BtcTransaction btcTransaction = createTransactionWithMultiplePegIns(federationSupport.getActiveFederation().getAddress(), btcPublicKey, minimumPeginValue, numberOfOutputs);
BtcTransaction btcTransaction = createTransactionWithMultiplePegIns(federationSupport.getActiveFederation().getAddress(), btcPublicKey, minimumPeginValue, numberOfOutputs, outputIndex, spendTxHashSeed);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
Expand All @@ -147,21 +149,19 @@ void registerBtcTransaction_forMultipleLegacyBtcTransaction_shouldRegisterTheNew
bridgeSupport.save();

// Assert
Optional<Long> heightIfBtcTxHashIsAlreadyProcessed = bridgeStorageProvider.getHeightIfBtcTxhashIsAlreadyProcessed(btcTransaction.getHash());
assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent());
assertEquals(RSK_EXECUTION_BLOCK_NUMBER, heightIfBtcTxHashIsAlreadyProcessed.get());
assertItWasProcessed(btcTransaction);

List<UTXO> expectedFederationUtxos = new ArrayList<>();
for (short outputIndex = 0; outputIndex < numberOfOutputs; outputIndex++) {
TransactionOutput output = btcTransaction.getOutput(outputIndex);
for (short newOutputIndex = 0; newOutputIndex < numberOfOutputs; newOutputIndex++) {
TransactionOutput output = btcTransaction.getOutput(newOutputIndex);
expectedFederationUtxos.add(utxoOf(btcTransaction, output));
}

assertEquals(expectedFederationUtxos, federationSupport.getActiveFederationBtcUTXOs());

co.rsk.core.Coin expectedReceiverBalance = co.rsk.core.Coin.ZERO;
for (short outputIndex = 0; outputIndex < numberOfOutputs; outputIndex++) {
TransactionOutput output = btcTransaction.getOutput(outputIndex);
for (short newOutputIndex = 0; newOutputIndex < numberOfOutputs; newOutputIndex++) {
TransactionOutput output = btcTransaction.getOutput(newOutputIndex);
expectedReceiverBalance = expectedReceiverBalance.add(co.rsk.core.Coin.fromBitcoin(output.getValue()));
}

Expand All @@ -174,7 +174,7 @@ void registerBtcTransaction_forMultipleLegacyBtcTransactionBelowMinimumWithSumAb
// Arrange
short numberOfOutputs = 2;
Coin partOfMinimumValue = Coin.valueOf(minimumPeginValue.getValue() / numberOfOutputs);
BtcTransaction btcTransaction = createTransactionWithMultiplePegIns(federationSupport.getActiveFederation().getAddress(), btcPublicKey, partOfMinimumValue, numberOfOutputs);
BtcTransaction btcTransaction = createTransactionWithMultiplePegIns(federationSupport.getActiveFederation().getAddress(), btcPublicKey, partOfMinimumValue, numberOfOutputs, outputIndex, spendTxHashSeed);
assertTrue((partOfMinimumValue.getValue() * numberOfOutputs) >= minimumPeginValue.getValue());

PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
Expand Down Expand Up @@ -202,7 +202,7 @@ void registerBtcTransaction_forMultipleLegacyBtcTransactionBelowMinimumWithSumAb
@Test
void registerBtcTransaction_forARepeatedLegacyBtcTransaction_shouldNotPerformAnyChange() throws Exception {
// Arrange
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey);
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey, spendTxHashSeed, outputIndex);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
Expand All @@ -227,7 +227,7 @@ void registerBtcTransaction_forARepeatedLegacyBtcTransaction_shouldNotPerformAny
@Test
void registerBtcTransaction_whenLegacyBtcTransactionWithNegativeHeight_shouldNotPerformAnyChange() throws Exception {
// Arrange
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey);
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey, spendTxHashSeed, outputIndex);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
Expand All @@ -251,7 +251,7 @@ void registerBtcTransaction_whenLegacyBtcTransactionWithNegativeHeight_shouldNot
void registerBtcTransaction_whenLegacyBtcTransactionWithBalanceBelowMinimum_shouldNotRefundFunds() throws Exception {
// Arrange
Coin valueBelowMinimumPegin = minimumPeginValue.subtract(Coin.SATOSHI);
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), valueBelowMinimumPegin, btcPublicKey);
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), valueBelowMinimumPegin, btcPublicKey, spendTxHashSeed, outputIndex);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
Expand All @@ -277,7 +277,7 @@ void registerBtcTransaction_whenLegacyBtcTransactionWithBalanceBelowMinimum_shou
@Test
void registerBtcTransaction_whenLegacyPeginBtcTransactionFromAMultiSig_shouldRefundTheFunds() throws Exception {
// Arrange
BtcTransaction btcTransaction = createMultiSigPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue);
BtcTransaction btcTransaction = createMultiSigPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, outputIndex, spendTxHashSeed);
PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
Expand Down Expand Up @@ -305,8 +305,8 @@ void registerBtcTransaction_whenLegacyPeginBtcTransactionFromAMultiSig_shouldRef

// Pegout value + fee == Pegin value
BtcTransaction pegOut = pegOutWaitingForConfirmationEntry.getBtcTransaction();
int outputIndex = 0;
TransactionOutput pegOutOutput = pegOut.getOutput(outputIndex);
int newOutputIndex = 0;
TransactionOutput pegOutOutput = pegOut.getOutput(newOutputIndex);
Coin pegOutTotalValue = pegOutOutput.getValue().add(pegOut.getFee());
assertEquals(minimumPeginValue, pegOutTotalValue);

Expand All @@ -321,9 +321,49 @@ void registerBtcTransaction_whenLegacyPeginBtcTransactionFromAMultiSig_shouldRef
assertEquals(expectedFederationUTXOs, federationSupport.getActiveFederationBtcUTXOs());
assertEquals(expectedReceiverBalance, repository.getBalance(rskReceiver));

Optional<Long> heightIfBtcTxHashIsAlreadyProcessed = bridgeStorageProvider.getHeightIfBtcTxhashIsAlreadyProcessed(btcTransaction.getHash());
assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent());
assertEquals(RSK_EXECUTION_BLOCK_NUMBER, heightIfBtcTxHashIsAlreadyProcessed.get());
assertItWasProcessed(btcTransaction);
}

@Test
void registerBtcTransaction_forALegacyBtcTransactionWithMultipleInputs_shouldRegisterTheNewUtxoAndTransferTheRbtcBalanceToTheFirstInputAddress() throws Exception {
// Arrange
BtcTransaction btcTransaction = createPegInTransaction(federationSupport.getActiveFederation().getAddress(), minimumPeginValue, btcPublicKey, outputIndex, spendTxHashSeed);

List<BtcECKey> fedKeys = BitcoinTestUtils.getBtcEcKeysFromSeeds(new String[]{"seed1", "seed2"}, true);
for (BtcECKey fedKey : fedKeys) {
spendTxHashSeed++;
outputIndex++;
btcTransaction.addInput(BitcoinTestUtils.createHash(spendTxHashSeed), outputIndex, ScriptBuilder.createInputScript(null, fedKey));
}

PartialMerkleTree pmtWithTransactions = createValidPmtForTransactions(List.of(btcTransaction.getHash()), btcNetworkParams);
int btcBlockWithPmtHeight = bridgeConstants.getBtcHeightWhenPegoutTxIndexActivates() + bridgeConstants.getPegoutTxIndexGracePeriodInBtcBlocks();
int chainHeight = btcBlockWithPmtHeight + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations();
recreateChainFromPmt(btcBlockStoreWithCache, chainHeight, pmtWithTransactions, btcBlockWithPmtHeight, btcNetworkParams);
bridgeStorageProvider.save();

// Act
bridgeSupport.registerBtcTransaction(rskTx, btcTransaction.bitcoinSerialize(), btcBlockWithPmtHeight, pmtWithTransactions.bitcoinSerialize());
bridgeSupport.save();

// Assert
assertItWasProcessed(btcTransaction);

int newOutputIndex = 0;
TransactionOutput output = btcTransaction.getOutput(newOutputIndex);
List<UTXO> expectedFederationUtxos = List.of(utxoOf(btcTransaction, output));
assertEquals(expectedFederationUtxos, federationSupport.getActiveFederationBtcUTXOs());

co.rsk.core.Coin expectedReceiverBalance = co.rsk.core.Coin.fromBitcoin(output.getValue());
assertEquals(expectedReceiverBalance, repository.getBalance(rskReceiver));

for (BtcECKey fedKey : fedKeys) {
ECKey ecKey = ECKey.fromPublicOnly(fedKey.getPubKey());
co.rsk.core.Coin expectedNonReceiverBalance = co.rsk.core.Coin.ZERO;
assertEquals(expectedNonReceiverBalance, repository.getBalance(new RskAddress(ecKey.getAddress())));
}

assertLogPegInBtc(btcTransaction, minimumPeginValue.getValue());
}

private static UTXO utxoOf(BtcTransaction btcTransaction, TransactionOutput output) {
Expand All @@ -338,40 +378,41 @@ private static UTXO utxoOf(BtcTransaction btcTransaction, TransactionOutput outp
);
}

private BtcTransaction createPegInTransaction(Address federationAddress, Coin coin, BtcECKey pubKey) {
private BtcTransaction createPegInTransaction(Address federationAddress, Coin coin, BtcECKey pubKey, int spendTxHashSeed, int outputIndex) {
BtcTransaction btcTx = new BtcTransaction(btcNetworkParams);
int outputIndex = 0;
int nHash = 0;
btcTx.addInput(BitcoinTestUtils.createHash(nHash), outputIndex, ScriptBuilder.createInputScript(null, pubKey));
btcTx.addInput(BitcoinTestUtils.createHash(spendTxHashSeed), outputIndex, ScriptBuilder.createInputScript(null, pubKey));
btcTx.addOutput(new TransactionOutput(btcNetworkParams, btcTx, coin, federationAddress));

return btcTx;
}

private BtcTransaction createTransactionWithMultiplePegIns(Address federationAddress, BtcECKey pubKey, Coin value, short numberOfOutputs) {
private BtcTransaction createTransactionWithMultiplePegIns(Address federationAddress, BtcECKey pubKey, Coin value, short numberOfOutputs, int outputIndex, int spendTxHashSeed) {
BtcTransaction btcTx = new BtcTransaction(btcNetworkParams);
int outputIndex = 0;
int nHash = 0;
btcTx.addInput(BitcoinTestUtils.createHash(nHash), outputIndex, ScriptBuilder.createInputScript(null, pubKey));
btcTx.addInput(BitcoinTestUtils.createHash(spendTxHashSeed), outputIndex, ScriptBuilder.createInputScript(null, pubKey));
for (int i = 0; i < numberOfOutputs; i++) {
btcTx.addOutput(new TransactionOutput(btcNetworkParams, btcTx, value, federationAddress));
}
return btcTx;
}

private BtcTransaction createMultiSigPegInTransaction(Address federationAddress, Coin coin) {
private BtcTransaction createMultiSigPegInTransaction(Address federationAddress, Coin coin, int outputIndex, int spendTxHashSeed) {
BtcTransaction btcTx = new BtcTransaction(btcNetworkParams);

btcTx.addInput(
BitcoinTestUtils.createHash(1),
0,
BitcoinTestUtils.createHash(spendTxHashSeed),
outputIndex,
ScriptBuilder.createP2SHMultiSigInputScript(null, federationSupport.getActiveFederation().getRedeemScript())
);
btcTx.addOutput(new TransactionOutput(btcNetworkParams, btcTx, coin, federationAddress));

return btcTx;
}

private void assertItWasProcessed(BtcTransaction btcTransaction) throws IOException {
Optional<Long> heightIfBtcTxHashIsAlreadyProcessed = bridgeStorageProvider.getHeightIfBtcTxhashIsAlreadyProcessed(btcTransaction.getHash());
assertTrue(heightIfBtcTxHashIsAlreadyProcessed.isPresent());
assertEquals(RSK_EXECUTION_BLOCK_NUMBER, heightIfBtcTxHashIsAlreadyProcessed.get());
}

private void assertLogPegInBtc(BtcTransaction btcTransaction, long value) {
CallTransaction.Function pegInBtcEvent = BridgeEvents.PEGIN_BTC.getEvent();
Sha256Hash peginTransactionHash = btcTransaction.getHash();
Expand Down
Loading