Search in sources :

Example 1 with TransactionInput

use of co.rsk.bitcoinj.core.TransactionInput in project rskj by rsksmart.

the class BridgeSupport method processSigning.

private void processSigning(BtcECKey federatorPublicKey, List<byte[]> signatures, byte[] rskTxHash, BtcTransaction btcTx, Federation federation) throws IOException {
    // Build input hashes for signatures
    int numInputs = btcTx.getInputs().size();
    List<Sha256Hash> sighashes = new ArrayList<>();
    List<TransactionSignature> txSigs = new ArrayList<>();
    for (int i = 0; i < numInputs; i++) {
        TransactionInput txIn = btcTx.getInput(i);
        Script inputScript = txIn.getScriptSig();
        List<ScriptChunk> chunks = inputScript.getChunks();
        byte[] program = chunks.get(chunks.size() - 1).data;
        Script redeemScript = new Script(program);
        sighashes.add(btcTx.hashForSignature(i, redeemScript, BtcTransaction.SigHash.ALL, false));
    }
    // Verify given signatures are correct before proceeding
    for (int i = 0; i < numInputs; i++) {
        BtcECKey.ECDSASignature sig;
        try {
            sig = BtcECKey.ECDSASignature.decodeFromDER(signatures.get(i));
        } catch (RuntimeException e) {
            logger.warn("Malformed signature for input {} of tx {}: {}", i, new Keccak256(rskTxHash), ByteUtil.toHexString(signatures.get(i)));
            return;
        }
        Sha256Hash sighash = sighashes.get(i);
        if (!federatorPublicKey.verify(sighash, sig)) {
            logger.warn("Signature {} {} is not valid for hash {} and public key {}", i, ByteUtil.toHexString(sig.encodeToDER()), sighash, federatorPublicKey);
            return;
        }
        TransactionSignature txSig = new TransactionSignature(sig, BtcTransaction.SigHash.ALL, false);
        txSigs.add(txSig);
        if (!txSig.isCanonical()) {
            logger.warn("Signature {} {} is not canonical.", i, ByteUtil.toHexString(signatures.get(i)));
            return;
        }
    }
    // All signatures are correct. Proceed to signing
    for (int i = 0; i < numInputs; i++) {
        Sha256Hash sighash = sighashes.get(i);
        TransactionInput input = btcTx.getInput(i);
        Script inputScript = input.getScriptSig();
        boolean alreadySignedByThisFederator = BridgeUtils.isInputSignedByThisFederator(federatorPublicKey, sighash, input);
        // Sign the input if it wasn't already
        if (!alreadySignedByThisFederator) {
            try {
                int sigIndex = inputScript.getSigInsertionIndex(sighash, federatorPublicKey);
                inputScript = ScriptBuilder.updateScriptWithSignature(inputScript, txSigs.get(i).encodeToBitcoin(), sigIndex, 1, 1);
                input.setScriptSig(inputScript);
                logger.debug("Tx input {} for tx {} signed.", i, new Keccak256(rskTxHash));
            } catch (IllegalStateException e) {
                Federation retiringFederation = getRetiringFederation();
                if (getActiveFederation().hasBtcPublicKey(federatorPublicKey)) {
                    logger.debug("A member of the active federation is trying to sign a tx of the retiring one");
                    return;
                } else if (retiringFederation != null && retiringFederation.hasBtcPublicKey(federatorPublicKey)) {
                    logger.debug("A member of the retiring federation is trying to sign a tx of the active one");
                    return;
                }
                throw e;
            }
        } else {
            logger.warn("Input {} of tx {} already signed by this federator.", i, new Keccak256(rskTxHash));
            break;
        }
    }
    if (BridgeUtils.hasEnoughSignatures(btcContext, btcTx)) {
        logger.info("Tx fully signed {}. Hex: {}", btcTx, Hex.toHexString(btcTx.bitcoinSerialize()));
        provider.getRskTxsWaitingForSignatures().remove(new Keccak256(rskTxHash));
        eventLogger.logReleaseBtc(btcTx, rskTxHash);
    } else if (logger.isDebugEnabled()) {
        int missingSignatures = BridgeUtils.countMissingSignatures(btcContext, btcTx);
        int neededSignatures = federation.getNumberOfSignaturesRequired();
        int signaturesCount = neededSignatures - missingSignatures;
        logger.debug("Tx {} not yet fully signed. Requires {}/{} signatures but has {}", new Keccak256(rskTxHash), neededSignatures, getFederationSize(), signaturesCount);
    }
}
Also used : Script(co.rsk.bitcoinj.script.Script) Sha256Hash(co.rsk.bitcoinj.core.Sha256Hash) ArrayList(java.util.ArrayList) TransactionSignature(co.rsk.bitcoinj.crypto.TransactionSignature) Keccak256(co.rsk.crypto.Keccak256) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk) BtcECKey(co.rsk.bitcoinj.core.BtcECKey) TransactionInput(co.rsk.bitcoinj.core.TransactionInput)

Example 2 with TransactionInput

use of co.rsk.bitcoinj.core.TransactionInput in project rskj by rsksmart.

the class P2PkhBtcLockSenderTest method doesnt_parse_if_tx_doesnt_have_scriptsig.

@Test
public void doesnt_parse_if_tx_doesnt_have_scriptsig() {
    BtcTransaction btcTx = new BtcTransaction(networkParameters);
    btcTx.addInput(new TransactionInput(networkParameters, null, null));
    BtcLockSender btcLockSender = new P2pkhBtcLockSender();
    Assert.assertFalse(btcLockSender.tryParse(btcTx));
}
Also used : BtcTransaction(co.rsk.bitcoinj.core.BtcTransaction) TransactionInput(co.rsk.bitcoinj.core.TransactionInput) Test(org.junit.Test)

Example 3 with TransactionInput

use of co.rsk.bitcoinj.core.TransactionInput in project rskj by rsksmart.

the class P2PkhBtcLockSenderTest method doesnt_parse_if_tx_scriptsig_doesnt_have_two_chunks.

@Test
public void doesnt_parse_if_tx_scriptsig_doesnt_have_two_chunks() {
    BtcTransaction btcTx = new BtcTransaction(networkParameters);
    btcTx.addInput(new TransactionInput(networkParameters, null, new byte[] { 0x00 }));
    BtcLockSender btcLockSender = new P2pkhBtcLockSender();
    Assert.assertFalse(btcLockSender.tryParse(btcTx));
}
Also used : BtcTransaction(co.rsk.bitcoinj.core.BtcTransaction) TransactionInput(co.rsk.bitcoinj.core.TransactionInput) Test(org.junit.Test)

Example 4 with TransactionInput

use of co.rsk.bitcoinj.core.TransactionInput in project rskj by rsksmart.

the class BridgeUtilsTest method isInputSignedByThisFederator_notSigned.

@Test
public void isInputSignedByThisFederator_notSigned() {
    // Arrange
    BtcECKey federator1Key = new BtcECKey();
    BtcECKey federator2Key = new BtcECKey();
    Federation federation = new Federation(FederationMember.getFederationMembersFromKeys(Arrays.asList(federator1Key, federator2Key)), Instant.now(), 0, networkParameters);
    // Create a tx from the Fed to a random btc address
    BtcTransaction tx = new BtcTransaction(networkParameters);
    TransactionInput txInput = new TransactionInput(networkParameters, tx, new byte[] {}, new TransactionOutPoint(networkParameters, 0, Sha256Hash.ZERO_HASH));
    // Create script to be signed by federation members
    Script inputScript = PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation(federation);
    txInput.setScriptSig(inputScript);
    tx.addInput(txInput);
    List<ScriptChunk> chunks = inputScript.getChunks();
    byte[] program = chunks.get(chunks.size() - 1).data;
    Script redeemScript = new Script(program);
    Sha256Hash sighash = tx.hashForSignature(0, redeemScript, BtcTransaction.SigHash.ALL, false);
    // Act
    boolean isSigned = BridgeUtils.isInputSignedByThisFederator(federator1Key, sighash, txInput);
    // Assert
    Assert.assertFalse(isSigned);
}
Also used : Script(co.rsk.bitcoinj.script.Script) BtcTransaction(co.rsk.bitcoinj.core.BtcTransaction) Sha256Hash(co.rsk.bitcoinj.core.Sha256Hash) BtcECKey(co.rsk.bitcoinj.core.BtcECKey) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk) TransactionInput(co.rsk.bitcoinj.core.TransactionInput) TransactionOutPoint(co.rsk.bitcoinj.core.TransactionOutPoint) ActivationConfigsForTest(org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest) Test(org.junit.Test)

Example 5 with TransactionInput

use of co.rsk.bitcoinj.core.TransactionInput in project rskj by rsksmart.

the class BridgeUtilsTest method testIsMigrationTx.

@Test
public void testIsMigrationTx() {
    Context btcContext = new Context(networkParameters);
    List<BtcECKey> activeFederationKeys = Stream.of(BtcECKey.fromPrivate(Hex.decode("fa01")), BtcECKey.fromPrivate(Hex.decode("fa02"))).sorted(BtcECKey.PUBKEY_COMPARATOR).collect(Collectors.toList());
    Federation activeFederation = new Federation(FederationTestUtils.getFederationMembersWithBtcKeys(activeFederationKeys), Instant.ofEpochMilli(2000L), 2L, networkParameters);
    List<BtcECKey> retiringFederationKeys = Stream.of(BtcECKey.fromPrivate(Hex.decode("fb01")), BtcECKey.fromPrivate(Hex.decode("fb02")), BtcECKey.fromPrivate(Hex.decode("fb03"))).sorted(BtcECKey.PUBKEY_COMPARATOR).collect(Collectors.toList());
    Federation retiringFederation = new Federation(FederationTestUtils.getFederationMembersWithBtcKeys(retiringFederationKeys), Instant.ofEpochMilli(1000L), 1L, networkParameters);
    List<BtcECKey> retiredFederationKeys = Stream.of(BtcECKey.fromPrivate(Hex.decode("fc01")), BtcECKey.fromPrivate(Hex.decode("fc02"))).sorted(BtcECKey.PUBKEY_COMPARATOR).collect(Collectors.toList());
    Federation retiredFederation = new Federation(FederationTestUtils.getFederationMembersWithBtcKeys(retiredFederationKeys), Instant.ofEpochMilli(1000L), 1L, networkParameters);
    Address activeFederationAddress = activeFederation.getAddress();
    BtcTransaction migrationTx = new BtcTransaction(networkParameters);
    migrationTx.addOutput(Coin.COIN, activeFederationAddress);
    TransactionInput migrationTxInput = new TransactionInput(networkParameters, migrationTx, new byte[] {}, new TransactionOutPoint(networkParameters, 0, Sha256Hash.ZERO_HASH));
    migrationTx.addInput(migrationTxInput);
    signWithNecessaryKeys(retiringFederation, retiringFederationKeys, migrationTxInput, migrationTx);
    assertTrue(BridgeUtils.isMigrationTx(migrationTx, activeFederation, retiringFederation, null, btcContext, bridgeConstantsRegtest, activations));
    BtcTransaction toActiveFederationTx = new BtcTransaction(networkParameters);
    toActiveFederationTx.addOutput(Coin.COIN, activeFederationAddress);
    toActiveFederationTx.addInput(Sha256Hash.ZERO_HASH, 0, new Script(new byte[] {}));
    assertFalse(BridgeUtils.isMigrationTx(toActiveFederationTx, activeFederation, retiringFederation, null, btcContext, bridgeConstantsRegtest, activations));
    Address randomAddress = PegTestUtils.createRandomBtcAddress();
    BtcTransaction fromRetiringFederationTx = new BtcTransaction(networkParameters);
    fromRetiringFederationTx.addOutput(Coin.COIN, randomAddress);
    TransactionInput fromRetiringFederationTxInput = new TransactionInput(networkParameters, fromRetiringFederationTx, new byte[] {}, new TransactionOutPoint(networkParameters, 0, Sha256Hash.ZERO_HASH));
    fromRetiringFederationTx.addInput(fromRetiringFederationTxInput);
    signWithNecessaryKeys(retiringFederation, retiringFederationKeys, fromRetiringFederationTxInput, fromRetiringFederationTx);
    assertFalse(BridgeUtils.isMigrationTx(fromRetiringFederationTx, activeFederation, retiringFederation, null, btcContext, bridgeConstantsRegtest, activations));
    assertFalse(BridgeUtils.isMigrationTx(migrationTx, activeFederation, null, null, btcContext, bridgeConstantsRegtest, activations));
    BtcTransaction retiredMigrationTx = new BtcTransaction(networkParameters);
    retiredMigrationTx.addOutput(Coin.COIN, activeFederationAddress);
    TransactionInput retiredMigrationTxInput = new TransactionInput(networkParameters, retiredMigrationTx, new byte[] {}, new TransactionOutPoint(networkParameters, 0, Sha256Hash.ZERO_HASH));
    retiredMigrationTx.addInput(retiredMigrationTxInput);
    signWithNecessaryKeys(retiredFederation, retiredFederationKeys, retiredMigrationTxInput, retiredMigrationTx);
    Script p2SHScript = retiredFederation.getP2SHScript();
    assertTrue(BridgeUtils.isMigrationTx(retiredMigrationTx, activeFederation, null, p2SHScript, btcContext, bridgeConstantsRegtest, activations));
    assertTrue(BridgeUtils.isMigrationTx(retiredMigrationTx, activeFederation, retiringFederation, p2SHScript, btcContext, bridgeConstantsRegtest, activations));
    assertFalse(BridgeUtils.isMigrationTx(toActiveFederationTx, activeFederation, null, p2SHScript, btcContext, bridgeConstantsRegtest, activations));
    assertFalse(BridgeUtils.isMigrationTx(toActiveFederationTx, activeFederation, retiringFederation, p2SHScript, btcContext, bridgeConstantsRegtest, activations));
}
Also used : Context(co.rsk.bitcoinj.core.Context) Script(co.rsk.bitcoinj.script.Script) Address(co.rsk.bitcoinj.core.Address) RskAddress(co.rsk.core.RskAddress) BtcTransaction(co.rsk.bitcoinj.core.BtcTransaction) BtcECKey(co.rsk.bitcoinj.core.BtcECKey) TransactionInput(co.rsk.bitcoinj.core.TransactionInput) TransactionOutPoint(co.rsk.bitcoinj.core.TransactionOutPoint) ActivationConfigsForTest(org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest) Test(org.junit.Test)

Aggregations

TransactionInput (co.rsk.bitcoinj.core.TransactionInput)27 BtcTransaction (co.rsk.bitcoinj.core.BtcTransaction)25 Test (org.junit.Test)24 TransactionOutPoint (co.rsk.bitcoinj.core.TransactionOutPoint)22 ActivationConfigsForTest (org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest)22 BtcECKey (co.rsk.bitcoinj.core.BtcECKey)19 Script (co.rsk.bitcoinj.script.Script)16 Address (co.rsk.bitcoinj.core.Address)13 RskAddress (co.rsk.core.RskAddress)13 Context (co.rsk.bitcoinj.core.Context)12 Sha256Hash (co.rsk.bitcoinj.core.Sha256Hash)5 ScriptChunk (co.rsk.bitcoinj.script.ScriptChunk)5 TransactionSignature (co.rsk.bitcoinj.crypto.TransactionSignature)4 Coin (co.rsk.bitcoinj.core.Coin)3 ConsensusRule (org.ethereum.config.blockchain.upgrades.ConsensusRule)3 ScriptBuilder (co.rsk.bitcoinj.script.ScriptBuilder)2 Wallet (co.rsk.bitcoinj.wallet.Wallet)2 Keccak256 (co.rsk.crypto.Keccak256)2 ActivationConfig (org.ethereum.config.blockchain.upgrades.ActivationConfig)2 AddressFormatException (co.rsk.bitcoinj.core.AddressFormatException)1