Search in sources :

Example 11 with Script

use of co.rsk.bitcoinj.script.Script in project rskj by rsksmart.

the class UpdateCollectionsTest method updateCollections_confirmTxs.

private void updateCollections_confirmTxs(ExecutionStats stats, int numCases) throws IOException {
    final int minTxsWaitingForSigs = 0;
    final int maxTxsWaitingForSigs = 10;
    final int minReleaseTxs = 1;
    final int maxReleaseTxs = 100;
    final int minBlockNumber = 10;
    final int maxBlockNumber = 100;
    final int minHeight = 50;
    final int maxHeight = 150;
    final int minCentOutput = 1;
    final int maxCentOutput = 100;
    final NetworkParameters parameters = NetworkParameters.fromID(NetworkParameters.ID_REGTEST);
    BridgeStorageProviderInitializer storageInitializer = (BridgeStorageProvider provider, Repository repository, int executionIndex) -> {
        Random rnd = new Random();
        SortedMap<Keccak256, BtcTransaction> txsWaitingForSignatures;
        ReleaseTransactionSet txSet;
        try {
            txsWaitingForSignatures = provider.getRskTxsWaitingForSignatures();
        } catch (Exception e) {
            throw new RuntimeException("Unable to gather txs waiting for signatures");
        }
        try {
            txSet = provider.getReleaseTransactionSet();
        } catch (Exception e) {
            throw new RuntimeException("Unable to gather release tx set");
        }
        // Generate some txs waiting for signatures
        Script genesisFederationScript = bridgeConstants.getGenesisFederation().getP2SHScript();
        for (int i = 0; i < Helper.randomInRange(minTxsWaitingForSigs, maxTxsWaitingForSigs); i++) {
            Keccak256 rskHash = new Keccak256(HashUtil.keccak256(BigInteger.valueOf(rnd.nextLong()).toByteArray()));
            BtcTransaction btcTx = new BtcTransaction(networkParameters);
            Sha256Hash inputHash = Sha256Hash.wrap(HashUtil.sha256(BigInteger.valueOf(rnd.nextLong()).toByteArray()));
            btcTx.addInput(inputHash, 0, genesisFederationScript);
            btcTx.addOutput(Helper.randomCoin(Coin.CENT, minCentOutput, maxCentOutput), new BtcECKey());
            txsWaitingForSignatures.put(rskHash, btcTx);
        }
        // Generate some txs waiting for confirmations
        for (int i = 0; i < Helper.randomInRange(minReleaseTxs, maxReleaseTxs); i++) {
            BtcTransaction btcTx = new BtcTransaction(networkParameters);
            Sha256Hash inputHash = Sha256Hash.wrap(HashUtil.sha256(BigInteger.valueOf(rnd.nextLong()).toByteArray()));
            btcTx.addInput(inputHash, 0, genesisFederationScript);
            btcTx.addOutput(Helper.randomCoin(Coin.CENT, minCentOutput, maxCentOutput), new BtcECKey());
            long blockNumber = Helper.randomInRange(minBlockNumber, maxBlockNumber);
            txSet.add(btcTx, blockNumber);
        }
    };
    final byte[] updateCollectionsEncoded = Bridge.UPDATE_COLLECTIONS.encode();
    ABIEncoder abiEncoder = (int executionIndex) -> updateCollectionsEncoded;
    HeightProvider heightProvider = (int executionIndex) -> Helper.randomInRange(minHeight, maxHeight);
    executeAndAverage("updateCollections-releaseTxs", numCases, abiEncoder, storageInitializer, Helper.getZeroValueRandomSenderTxBuilder(), heightProvider, stats);
}
Also used : Script(co.rsk.bitcoinj.script.Script) BridgeStorageProvider(co.rsk.peg.BridgeStorageProvider) Keccak256(co.rsk.crypto.Keccak256) IOException(java.io.IOException) Repository(org.ethereum.core.Repository) Random(java.util.Random) ReleaseTransactionSet(co.rsk.peg.ReleaseTransactionSet) SortedMap(java.util.SortedMap)

Example 12 with Script

use of co.rsk.bitcoinj.script.Script in project rskj by rsksmart.

the class BridgeSupport method registerBtcTransaction.

/**
 * In case of a lock tx: Transfers some SBTCs to the sender of the btc tx and keeps track of the new UTXOs available for spending.
 * In case of a release tx: Keeps track of the change UTXOs, now available for spending.
 * @param btcTx The bitcoin transaction
 * @param height The height of the bitcoin block that contains the tx
 * @param pmt Partial Merklee Tree that proves the tx is included in the btc block
 * @throws BlockStoreException
 * @throws IOException
 */
public void registerBtcTransaction(Transaction rskTx, BtcTransaction btcTx, int height, PartialMerkleTree pmt) throws BlockStoreException, IOException {
    Context.propagate(btcContext);
    Federation federation = getActiveFederation();
    // Check the tx was not already processed
    if (provider.getBtcTxHashesAlreadyProcessed().keySet().contains(btcTx.getHash())) {
        logger.warn("Supplied tx was already processed");
        return;
    }
    // Check the tx is in the partial merkle tree
    List<Sha256Hash> hashesInPmt = new ArrayList<>();
    Sha256Hash merkleRoot = pmt.getTxnHashAndMerkleRoot(hashesInPmt);
    if (!hashesInPmt.contains(btcTx.getHash())) {
        logger.warn("Supplied tx is not in the supplied partial merkle tree");
        panicProcessor.panic("btclock", "Supplied tx is not in the supplied partial merkle tree");
        return;
    }
    if (height < 0) {
        logger.warn("Height is " + height + " but should be greater than 0");
        panicProcessor.panic("btclock", "Height is " + height + " but should be greater than 0");
        return;
    }
    // Check there are at least N blocks on top of the supplied height
    int headHeight = btcBlockChain.getBestChainHeight();
    if ((headHeight - height + 1) < bridgeConstants.getBtc2RskMinimumAcceptableConfirmations()) {
        logger.warn("At least " + bridgeConstants.getBtc2RskMinimumAcceptableConfirmations() + " confirmations are required, but there are only " + (headHeight - height) + " confirmations");
        return;
    }
    // Check the the merkle root equals merkle root of btc block at specified height in the btc best chain
    BtcBlock blockHeader = BridgeUtils.getStoredBlockAtHeight(btcBlockStore, height).getHeader();
    if (!blockHeader.getMerkleRoot().equals(merkleRoot)) {
        logger.warn("Supplied merkle root " + merkleRoot + "does not match block's merkle root " + blockHeader.getMerkleRoot());
        panicProcessor.panic("btclock", "Supplied merkle root " + merkleRoot + "does not match block's merkle root " + blockHeader.getMerkleRoot());
        return;
    }
    // Checks the transaction contents for sanity
    btcTx.verify();
    if (btcTx.getInputs().isEmpty()) {
        logger.warn("Tx has no inputs " + btcTx);
        panicProcessor.panic("btclock", "Tx has no inputs " + btcTx);
        return;
    }
    boolean locked = true;
    // Specific code for lock/release/none txs
    if (BridgeUtils.isLockTx(btcTx, getLiveFederations(), btcContext, bridgeConstants)) {
        logger.debug("This is a lock tx {}", btcTx);
        Script scriptSig = btcTx.getInput(0).getScriptSig();
        if (scriptSig.getChunks().size() != 2) {
            logger.warn("First input does not spend a Pay-to-PubkeyHash " + btcTx.getInput(0));
            panicProcessor.panic("btclock", "First input does not spend a Pay-to-PubkeyHash " + btcTx.getInput(0));
            return;
        }
        // Compute the total amount sent. Value could have been sent both to the
        // currently active federation as well as to the currently retiring federation.
        // Add both amounts up in that case.
        Coin amountToActive = btcTx.getValueSentToMe(getActiveFederationWallet());
        Coin amountToRetiring = Coin.ZERO;
        Wallet retiringFederationWallet = getRetiringFederationWallet();
        if (retiringFederationWallet != null) {
            amountToRetiring = btcTx.getValueSentToMe(retiringFederationWallet);
        }
        Coin totalAmount = amountToActive.add(amountToRetiring);
        // Get the sender public key
        byte[] data = scriptSig.getChunks().get(1).data;
        // Tx is a lock tx, check whether the sender is whitelisted
        BtcECKey senderBtcKey = BtcECKey.fromPublicOnly(data);
        Address senderBtcAddress = new Address(btcContext.getParams(), senderBtcKey.getPubKeyHash());
        // If the address is not whitelisted, then return the funds
        // using the exact same utxos sent to us.
        // That is, build a release transaction and get it in the release transaction set.
        // Otherwise, transfer SBTC to the sender of the BTC
        // The RSK account to update is the one that matches the pubkey "spent" on the first bitcoin tx input
        LockWhitelist lockWhitelist = provider.getLockWhitelist();
        if (!lockWhitelist.isWhitelistedFor(senderBtcAddress, totalAmount, height)) {
            locked = false;
            // Build the list of UTXOs in the BTC transaction sent to either the active
            // or retiring federation
            List<UTXO> utxosToUs = btcTx.getWalletOutputs(getNoSpendWalletForLiveFederations()).stream().map(output -> new UTXO(btcTx.getHash(), output.getIndex(), output.getValue(), 0, btcTx.isCoinBase(), output.getScriptPubKey())).collect(Collectors.toList());
            // Use the list of UTXOs to build a transaction builder
            // for the return btc transaction generation
            ReleaseTransactionBuilder txBuilder = new ReleaseTransactionBuilder(btcContext.getParams(), getUTXOBasedWalletForLiveFederations(utxosToUs), senderBtcAddress, getFeePerKb());
            Optional<ReleaseTransactionBuilder.BuildResult> buildReturnResult = txBuilder.buildEmptyWalletTo(senderBtcAddress);
            if (buildReturnResult.isPresent()) {
                provider.getReleaseTransactionSet().add(buildReturnResult.get().getBtcTx(), rskExecutionBlock.getNumber());
                logger.info("whitelist money return tx build successful to {}. Tx {}. Value {}.", senderBtcAddress, rskTx, totalAmount);
            } else {
                logger.warn("whitelist money return tx build for btc tx {} error. Return was to {}. Tx {}. Value {}", btcTx.getHash(), senderBtcAddress, rskTx, totalAmount);
                panicProcessor.panic("whitelist-return-funds", String.format("whitelist money return tx build for btc tx {} error. Return was to {}. Tx {}. Value {}", btcTx.getHash(), senderBtcAddress, rskTx, totalAmount));
            }
        } else {
            org.ethereum.crypto.ECKey key = org.ethereum.crypto.ECKey.fromPublicOnly(data);
            RskAddress sender = new RskAddress(key.getAddress());
            rskRepository.transfer(PrecompiledContracts.BRIDGE_ADDR, sender, co.rsk.core.Coin.fromBitcoin(totalAmount));
            lockWhitelist.remove(senderBtcAddress);
        }
    } else if (BridgeUtils.isReleaseTx(btcTx, federation, bridgeConstants)) {
        logger.debug("This is a release tx {}", btcTx);
    // do-nothing
    // We could call removeUsedUTXOs(btcTx) here, but we decided to not do that.
    // Used utxos should had been removed when we created the release tx.
    // Invoking removeUsedUTXOs() here would make "some" sense in theses scenarios:
    // a) In testnet, devnet or local: we restart the RSK blockchain whithout changing the federation address. We don't want to have utxos that were already spent.
    // Open problem: TxA spends TxB. registerBtcTransaction() for TxB is called, it spends a utxo the bridge is not yet aware of,
    // so nothing is removed. Then registerBtcTransaction() for TxA and the "already spent" utxo is added as it was not spent.
    // When is not guaranteed to be called in the chronological order, so a Federator can inform
    // b) In prod: Federator created a tx manually or the federation was compromised and some utxos were spent. Better not try to spend them.
    // Open problem: For performance removeUsedUTXOs() just removes 1 utxo
    } else if (BridgeUtils.isMigrationTx(btcTx, getActiveFederation(), getRetiringFederation(), btcContext, bridgeConstants)) {
        logger.debug("This is a migration tx {}", btcTx);
    } else {
        logger.warn("This is not a lock, a release nor a migration tx {}", btcTx);
        panicProcessor.panic("btclock", "This is not a lock, a release nor a migration tx " + btcTx);
        return;
    }
    Sha256Hash btcTxHash = btcTx.getHash();
    // Mark tx as processed on this block
    provider.getBtcTxHashesAlreadyProcessed().put(btcTxHash, rskExecutionBlock.getNumber());
    // locked the funds.
    if (locked) {
        saveNewUTXOs(btcTx);
    }
    logger.info("BTC Tx {} processed in RSK", btcTxHash);
}
Also used : java.util(java.util) Hex(org.spongycastle.util.encoders.Hex) LoggerFactory(org.slf4j.LoggerFactory) RskAddress(co.rsk.core.RskAddress) Keccak256(co.rsk.crypto.Keccak256) Block(org.ethereum.core.Block) TransactionSignature(co.rsk.bitcoinj.crypto.TransactionSignature) Pair(org.apache.commons.lang3.tuple.Pair) BridgeConstants(co.rsk.config.BridgeConstants) co.rsk.bitcoinj.core(co.rsk.bitcoinj.core) PrecompiledContracts(org.ethereum.vm.PrecompiledContracts) BigInteger(java.math.BigInteger) Nullable(javax.annotation.Nullable) Wallet(co.rsk.bitcoinj.wallet.Wallet) BtcBlockStore(co.rsk.bitcoinj.store.BtcBlockStore) PanicProcessor(co.rsk.panic.PanicProcessor) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk) Logger(org.slf4j.Logger) IOException(java.io.IOException) Instant(java.time.Instant) Repository(org.ethereum.core.Repository) Collectors(java.util.stream.Collectors) SendRequest(co.rsk.bitcoinj.wallet.SendRequest) Program(org.ethereum.vm.program.Program) ScriptBuilder(co.rsk.bitcoinj.script.ScriptBuilder) Script(co.rsk.bitcoinj.script.Script) BlockStoreException(co.rsk.bitcoinj.store.BlockStoreException) VisibleForTesting(com.google.common.annotations.VisibleForTesting) RskSystemProperties(co.rsk.config.RskSystemProperties) BridgeEventLogger(co.rsk.peg.utils.BridgeEventLogger) Transaction(org.ethereum.core.Transaction) InputStream(java.io.InputStream) Script(co.rsk.bitcoinj.script.Script) RskAddress(co.rsk.core.RskAddress) Wallet(co.rsk.bitcoinj.wallet.Wallet) RskAddress(co.rsk.core.RskAddress)

Example 13 with Script

use of co.rsk.bitcoinj.script.Script in project rskj by rsksmart.

the class BridgeSupport method hasEnoughSignatures.

/**
 * Checks whether a btc tx has been signed by the required number of federators.
 * @param btcTx The btc tx to check
 * @return True if was signed by the required number of federators, false otherwise
 */
private boolean hasEnoughSignatures(BtcTransaction btcTx) {
    // When the tx is constructed OP_0 are placed where signature should go.
    // Check all OP_0 have been replaced with actual signatures in all inputs
    Context.propagate(btcContext);
    for (TransactionInput input : btcTx.getInputs()) {
        Script scriptSig = input.getScriptSig();
        List<ScriptChunk> chunks = scriptSig.getChunks();
        for (int i = 1; i < chunks.size(); i++) {
            ScriptChunk chunk = chunks.get(i);
            if (!chunk.isOpCode() && chunk.data.length == 0) {
                return false;
            }
        }
    }
    return true;
}
Also used : Script(co.rsk.bitcoinj.script.Script) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk)

Example 14 with Script

use of co.rsk.bitcoinj.script.Script in project rskj by rsksmart.

the class BridgeSupportTest method registerBtcTransactionMigrationTx.

@Test
public void registerBtcTransactionMigrationTx() throws BlockStoreException, AddressFormatException, IOException {
    BridgeConstants bridgeConstants = BridgeRegTestConstants.getInstance();
    NetworkParameters parameters = bridgeConstants.getBtcParams();
    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(activeFederationKeys, Instant.ofEpochMilli(2000L), 2L, parameters);
    List<BtcECKey> retiringFederationKeys = Stream.of(BtcECKey.fromPrivate(Hex.decode("fb01")), BtcECKey.fromPrivate(Hex.decode("fb02"))).sorted(BtcECKey.PUBKEY_COMPARATOR).collect(Collectors.toList());
    Federation retiringFederation = new Federation(retiringFederationKeys, Instant.ofEpochMilli(1000L), 1L, parameters);
    Repository repository = new RepositoryImpl(config);
    repository.addBalance(PrecompiledContracts.BRIDGE_ADDR, LIMIT_MONETARY_BASE);
    Block executionBlock = Mockito.mock(Block.class);
    Mockito.when(executionBlock.getNumber()).thenReturn(15L);
    Repository track = repository.startTracking();
    BtcTransaction tx = new BtcTransaction(parameters);
    Address activeFederationAddress = activeFederation.getAddress();
    tx.addOutput(Coin.COIN, activeFederationAddress);
    // Create previous tx
    BtcTransaction prevTx = new BtcTransaction(btcParams);
    TransactionOutput prevOut = new TransactionOutput(btcParams, prevTx, Coin.FIFTY_COINS, retiringFederation.getAddress());
    prevTx.addOutput(prevOut);
    // Create tx input
    tx.addInput(prevOut);
    // Create tx input base script sig
    Script scriptSig = PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation(retiringFederation);
    // Create sighash
    Script redeemScript = ScriptBuilder.createRedeemScript(retiringFederation.getNumberOfSignaturesRequired(), retiringFederation.getPublicKeys());
    Sha256Hash sighash = tx.hashForSignature(0, redeemScript, BtcTransaction.SigHash.ALL, false);
    // Sign by federator 0
    BtcECKey.ECDSASignature sig0 = retiringFederationKeys.get(0).sign(sighash);
    TransactionSignature txSig0 = new TransactionSignature(sig0, BtcTransaction.SigHash.ALL, false);
    int sigIndex0 = scriptSig.getSigInsertionIndex(sighash, retiringFederation.getPublicKeys().get(0));
    scriptSig = ScriptBuilder.updateScriptWithSignature(scriptSig, txSig0.encodeToBitcoin(), sigIndex0, 1, 1);
    // Sign by federator 1
    BtcECKey.ECDSASignature sig1 = retiringFederationKeys.get(1).sign(sighash);
    TransactionSignature txSig1 = new TransactionSignature(sig1, BtcTransaction.SigHash.ALL, false);
    int sigIndex1 = scriptSig.getSigInsertionIndex(sighash, retiringFederation.getPublicKeys().get(1));
    scriptSig = ScriptBuilder.updateScriptWithSignature(scriptSig, txSig1.encodeToBitcoin(), sigIndex1, 1, 1);
    // Set scipt sign to tx input
    tx.getInput(0).setScriptSig(scriptSig);
    Context btcContext = new Context(bridgeConstants.getBtcParams());
    BtcBlockStore btcBlockStore = new RepositoryBlockStore(config, track, PrecompiledContracts.BRIDGE_ADDR);
    BtcBlockChain btcBlockChain = new SimpleBlockChain(btcContext, btcBlockStore);
    BridgeStorageProvider provider = new BridgeStorageProvider(track, contractAddress, config.getBlockchainConfig().getCommonConstants().getBridgeConstants());
    provider.setNewFederation(activeFederation);
    provider.setOldFederation(retiringFederation);
    BridgeSupport bridgeSupport = new BridgeSupport(config, track, null, config.getBlockchainConfig().getCommonConstants().getBridgeConstants(), provider, btcBlockStore, btcBlockChain);
    Whitebox.setInternalState(bridgeSupport, "rskExecutionBlock", executionBlock);
    byte[] bits = new byte[1];
    bits[0] = 0x3f;
    List<Sha256Hash> hashes = new ArrayList<>();
    hashes.add(tx.getHash());
    PartialMerkleTree pmt = new PartialMerkleTree(btcParams, bits, hashes, 1);
    List<Sha256Hash> hashlist = new ArrayList<>();
    Sha256Hash merkleRoot = pmt.getTxnHashAndMerkleRoot(hashlist);
    co.rsk.bitcoinj.core.BtcBlock block = new co.rsk.bitcoinj.core.BtcBlock(btcParams, 1, PegTestUtils.createHash(), merkleRoot, 1, 1, 1, new ArrayList<BtcTransaction>());
    btcBlockChain.add(block);
    ((SimpleBlockChain) btcBlockChain).useHighBlock();
    bridgeSupport.registerBtcTransaction(mock(Transaction.class), tx, 1, pmt);
    bridgeSupport.save();
    ((SimpleBlockChain) btcBlockChain).useBlock();
    track.commit();
    List<UTXO> activeFederationBtcUTXOs = provider.getNewFederationBtcUTXOs();
    List<Coin> activeFederationBtcCoins = activeFederationBtcUTXOs.stream().map(UTXO::getValue).collect(Collectors.toList());
    assertThat(activeFederationBtcUTXOs, hasSize(1));
    assertThat(activeFederationBtcCoins, hasItem(Coin.COIN));
}
Also used : RskAddress(co.rsk.core.RskAddress) TransactionSignature(co.rsk.bitcoinj.crypto.TransactionSignature) SimpleBlockChain(co.rsk.peg.simples.SimpleBlockChain) BridgeConstants(co.rsk.config.BridgeConstants) Script(co.rsk.bitcoinj.script.Script) BtcBlockStore(co.rsk.bitcoinj.store.BtcBlockStore) co.rsk.bitcoinj.core(co.rsk.bitcoinj.core) org.ethereum.core(org.ethereum.core) SimpleRskTransaction(co.rsk.peg.simples.SimpleRskTransaction) RepositoryImpl(co.rsk.db.RepositoryImpl) PrepareForTest(org.powermock.core.classloader.annotations.PrepareForTest) Test(org.junit.Test)

Example 15 with Script

use of co.rsk.bitcoinj.script.Script in project rskj by rsksmart.

the class BridgeSupportTest method addSignatureMultipleInputsPartiallyValid.

@Test
public void addSignatureMultipleInputsPartiallyValid() throws Exception {
    // Federation is the genesis federation ATM
    Federation federation = bridgeConstants.getGenesisFederation();
    Repository repository = new RepositoryImpl(config);
    final Keccak256 keccak256 = PegTestUtils.createHash3();
    Repository track = repository.startTracking();
    BridgeStorageProvider provider = new BridgeStorageProvider(track, PrecompiledContracts.BRIDGE_ADDR, bridgeConstants);
    BtcTransaction prevTx1 = new BtcTransaction(btcParams);
    TransactionOutput prevOut1 = new TransactionOutput(btcParams, prevTx1, Coin.FIFTY_COINS, federation.getAddress());
    prevTx1.addOutput(prevOut1);
    BtcTransaction prevTx2 = new BtcTransaction(btcParams);
    TransactionOutput prevOut2 = new TransactionOutput(btcParams, prevTx1, Coin.FIFTY_COINS, federation.getAddress());
    prevTx2.addOutput(prevOut2);
    BtcTransaction prevTx3 = new BtcTransaction(btcParams);
    TransactionOutput prevOut3 = new TransactionOutput(btcParams, prevTx1, Coin.FIFTY_COINS, federation.getAddress());
    prevTx3.addOutput(prevOut3);
    BtcTransaction t = new BtcTransaction(btcParams);
    TransactionOutput output = new TransactionOutput(btcParams, t, Coin.COIN, new BtcECKey().toAddress(btcParams));
    t.addOutput(output);
    t.addInput(prevOut1).setScriptSig(PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation(federation));
    t.addInput(prevOut2).setScriptSig(PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation(federation));
    t.addInput(prevOut3).setScriptSig(PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation(federation));
    provider.getRskTxsWaitingForSignatures().put(keccak256, t);
    provider.save();
    track.commit();
    track = repository.startTracking();
    List<LogInfo> logs = new ArrayList<>();
    BridgeEventLogger eventLogger = new BridgeEventLoggerImpl(bridgeConstants, logs);
    BridgeSupport bridgeSupport = new BridgeSupport(config, track, eventLogger, contractAddress, (Block) null);
    // Generate valid signatures for inputs
    List<byte[]> derEncodedSigsFirstFed = new ArrayList<>();
    List<byte[]> derEncodedSigsSecondFed = new ArrayList<>();
    BtcECKey privateKeyOfFirstFed = ((BridgeRegTestConstants) bridgeConstants).getFederatorPrivateKeys().get(0);
    BtcECKey privateKeyOfSecondFed = ((BridgeRegTestConstants) bridgeConstants).getFederatorPrivateKeys().get(1);
    BtcECKey.ECDSASignature lastSig = null;
    for (int i = 0; i < 3; i++) {
        Script inputScript = t.getInput(i).getScriptSig();
        List<ScriptChunk> chunks = inputScript.getChunks();
        byte[] program = chunks.get(chunks.size() - 1).data;
        Script redeemScript = new Script(program);
        Sha256Hash sighash = t.hashForSignature(i, redeemScript, BtcTransaction.SigHash.ALL, false);
        // Sign the last input with a random key
        // but keep the good signature for a subsequent call
        BtcECKey.ECDSASignature sig = privateKeyOfFirstFed.sign(sighash);
        if (i == 2) {
            lastSig = sig;
            sig = new BtcECKey().sign(sighash);
        }
        derEncodedSigsFirstFed.add(sig.encodeToDER());
        derEncodedSigsSecondFed.add(privateKeyOfSecondFed.sign(sighash).encodeToDER());
    }
    // Sign with two valid signatuers and one invalid signature
    bridgeSupport.addSignature(1, findPublicKeySignedBy(federation.getPublicKeys(), privateKeyOfFirstFed), derEncodedSigsFirstFed, keccak256.getBytes());
    bridgeSupport.save();
    track.commit();
    // Sign with two valid signatuers and one malformed signature
    byte[] malformedSignature = new byte[lastSig.encodeToDER().length];
    for (int i = 0; i < malformedSignature.length; i++) {
        malformedSignature[i] = (byte) i;
    }
    derEncodedSigsFirstFed.set(2, malformedSignature);
    bridgeSupport.addSignature(1, findPublicKeySignedBy(federation.getPublicKeys(), privateKeyOfFirstFed), derEncodedSigsFirstFed, keccak256.getBytes());
    bridgeSupport.save();
    track.commit();
    // Sign with fully valid signatures for same federator
    derEncodedSigsFirstFed.set(2, lastSig.encodeToDER());
    bridgeSupport.addSignature(1, findPublicKeySignedBy(federation.getPublicKeys(), privateKeyOfFirstFed), derEncodedSigsFirstFed, keccak256.getBytes());
    bridgeSupport.save();
    track.commit();
    // Sign with second federation
    bridgeSupport.addSignature(1, findPublicKeySignedBy(federation.getPublicKeys(), privateKeyOfSecondFed), derEncodedSigsSecondFed, keccak256.getBytes());
    bridgeSupport.save();
    track.commit();
    provider = new BridgeStorageProvider(repository, PrecompiledContracts.BRIDGE_ADDR, bridgeConstants);
    Assert.assertTrue(provider.getRskTxsWaitingForSignatures().isEmpty());
    Assert.assertThat(logs, is(not(empty())));
    Assert.assertThat(logs, hasSize(5));
    LogInfo releaseTxEvent = logs.get(4);
    Assert.assertThat(releaseTxEvent.getTopics(), hasSize(1));
    Assert.assertThat(releaseTxEvent.getTopics(), hasItem(Bridge.RELEASE_BTC_TOPIC));
    BtcTransaction releaseTx = new BtcTransaction(bridgeConstants.getBtcParams(), ((RLPList) RLP.decode2(releaseTxEvent.getData()).get(0)).get(1).getRLPData());
    // Verify all inputs fully signed
    for (int i = 0; i < releaseTx.getInputs().size(); i++) {
        Script retrievedScriptSig = releaseTx.getInput(i).getScriptSig();
        Assert.assertEquals(4, retrievedScriptSig.getChunks().size());
        Assert.assertEquals(true, retrievedScriptSig.getChunks().get(1).data.length > 0);
        Assert.assertEquals(true, retrievedScriptSig.getChunks().get(2).data.length > 0);
    }
}
Also used : BridgeEventLoggerImpl(co.rsk.peg.utils.BridgeEventLoggerImpl) Script(co.rsk.bitcoinj.script.Script) LogInfo(org.ethereum.vm.LogInfo) Keccak256(co.rsk.crypto.Keccak256) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk) RLPList(org.ethereum.util.RLPList) RepositoryImpl(co.rsk.db.RepositoryImpl) BridgeEventLogger(co.rsk.peg.utils.BridgeEventLogger) PrepareForTest(org.powermock.core.classloader.annotations.PrepareForTest) Test(org.junit.Test)

Aggregations

Script (co.rsk.bitcoinj.script.Script)18 ScriptChunk (co.rsk.bitcoinj.script.ScriptChunk)8 Test (org.junit.Test)7 RskAddress (co.rsk.core.RskAddress)6 Keccak256 (co.rsk.crypto.Keccak256)6 TransactionSignature (co.rsk.bitcoinj.crypto.TransactionSignature)5 RepositoryImpl (co.rsk.db.RepositoryImpl)5 BridgeRegTestConstants (co.rsk.config.BridgeRegTestConstants)4 BridgeEventLogger (co.rsk.peg.utils.BridgeEventLogger)4 PrepareForTest (org.powermock.core.classloader.annotations.PrepareForTest)4 co.rsk.bitcoinj.core (co.rsk.bitcoinj.core)3 BtcBlockStore (co.rsk.bitcoinj.store.BtcBlockStore)3 BridgeEventLoggerImpl (co.rsk.peg.utils.BridgeEventLoggerImpl)3 IOException (java.io.IOException)3 Repository (org.ethereum.core.Repository)3 RLPList (org.ethereum.util.RLPList)3 LogInfo (org.ethereum.vm.LogInfo)3 Wallet (co.rsk.bitcoinj.wallet.Wallet)2 BridgeConstants (co.rsk.config.BridgeConstants)2 BridgeStorageProvider (co.rsk.peg.BridgeStorageProvider)2