Search in sources :

Example 6 with BlockStoreException

use of co.rsk.bitcoinj.store.BlockStoreException in project rskj by rsksmart.

the class BridgeSupport method getBtcBlockchainBlockLocator.

/**
 * @deprecated
 * Returns an array of block hashes known by the bridge contract.
 * Federators can use this to find what is the latest block in the mainchain the bridge has.
 * @return a List of bitcoin block hashes
 */
@Deprecated
public List<Sha256Hash> getBtcBlockchainBlockLocator() throws IOException, BlockStoreException {
    StoredBlock initialBtcStoredBlock = this.getLowestBlock();
    final int maxHashesToInform = 100;
    List<Sha256Hash> blockLocator = new ArrayList<>();
    StoredBlock cursor = getBtcBlockchainChainHead();
    int bestBlockHeight = cursor.getHeight();
    blockLocator.add(cursor.getHeader().getHash());
    if (bestBlockHeight > initialBtcStoredBlock.getHeight()) {
        boolean stop = false;
        int i = 0;
        try {
            while (blockLocator.size() <= maxHashesToInform && !stop) {
                int blockHeight = (int) (bestBlockHeight - Math.pow(2, i));
                if (blockHeight <= initialBtcStoredBlock.getHeight()) {
                    blockLocator.add(initialBtcStoredBlock.getHeader().getHash());
                    stop = true;
                } else {
                    cursor = this.getPrevBlockAtHeight(cursor, blockHeight);
                    blockLocator.add(cursor.getHeader().getHash());
                }
                i++;
            }
        } catch (Exception e) {
            logger.error("Failed to walk the block chain whilst constructing a locator");
            panicProcessor.panic("btcblockchain", "Failed to walk the block chain whilst constructing a locator");
            throw new RuntimeException(e);
        }
        if (!stop) {
            blockLocator.add(initialBtcStoredBlock.getHeader().getHash());
        }
    }
    return blockLocator;
}
Also used : StoredBlock(co.rsk.bitcoinj.core.StoredBlock) Sha256Hash(co.rsk.bitcoinj.core.Sha256Hash) ArrayList(java.util.ArrayList) VMException(org.ethereum.vm.exception.VMException) UTXOProviderException(co.rsk.bitcoinj.core.UTXOProviderException) VerificationException(co.rsk.bitcoinj.core.VerificationException) InsufficientMoneyException(co.rsk.bitcoinj.core.InsufficientMoneyException) AddressFormatException(co.rsk.bitcoinj.core.AddressFormatException) IOException(java.io.IOException) BlockStoreException(co.rsk.bitcoinj.store.BlockStoreException) PeginInstructionsException(co.rsk.peg.pegininstructions.PeginInstructionsException)

Example 7 with BlockStoreException

use of co.rsk.bitcoinj.store.BlockStoreException in project rskj by rsksmart.

the class BridgeSupportTestPowerMock method getBtcTransactionConfirmationsGetCost_getBestChainHeightError.

@Test
public void getBtcTransactionConfirmationsGetCost_getBestChainHeightError() throws BlockStoreException {
    ActivationConfig.ForBlock activations = mock(ActivationConfig.ForBlock.class);
    Repository repository = createRepository();
    Repository track = repository.startTracking();
    Sha256Hash blockHash = Sha256Hash.of(Hex.decode("aabbcc"));
    StoredBlock block = new StoredBlock(null, new BigInteger("0"), 50);
    BtcBlockStoreWithCache btcBlockStore = mock(BtcBlockStoreWithCache.class);
    when(btcBlockStore.getFromCache(blockHash)).thenReturn(block);
    BridgeStorageProvider provider = new BridgeStorageProvider(track, PrecompiledContracts.BRIDGE_ADDR, bridgeConstants, activationsBeforeForks);
    BtcBlockStoreWithCache.Factory mockFactory = mock(BtcBlockStoreWithCache.Factory.class);
    when(mockFactory.newInstance(track, bridgeConstants, provider, activations)).thenReturn(btcBlockStore);
    when(btcBlockStore.getChainHead()).thenThrow(new BlockStoreException(""));
    BridgeSupport bridgeSupport = getBridgeSupport(provider, track, mockFactory, activations);
    Object[] args = new Object[4];
    args[1] = blockHash.getBytes();
    long cost = bridgeSupport.getBtcTransactionConfirmationsGetCost(args);
    Assert.assertEquals(27_000, cost);
}
Also used : BlockStoreException(co.rsk.bitcoinj.store.BlockStoreException) ActivationConfig(org.ethereum.config.blockchain.upgrades.ActivationConfig) MutableRepository(org.ethereum.db.MutableRepository) BigInteger(java.math.BigInteger) ActivationConfigsForTest(org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest) PrepareForTest(org.powermock.core.classloader.annotations.PrepareForTest) Test(org.junit.Test)

Example 8 with BlockStoreException

use of co.rsk.bitcoinj.store.BlockStoreException 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 9 with BlockStoreException

use of co.rsk.bitcoinj.store.BlockStoreException in project rskj by rsksmart.

the class BtcBlockchainTest method buildInitializer.

private BridgeStorageProviderInitializer buildInitializer() {
    final int minBtcBlocks = 1000;
    final int maxBtcBlocks = 2000;
    return (BridgeStorageProvider provider, Repository repository, int executionIndex) -> {
        BtcBlockStore btcBlockStore = new RepositoryBlockStore(new RskSystemProperties(), repository, PrecompiledContracts.BRIDGE_ADDR);
        Context btcContext = new Context(networkParameters);
        BtcBlockChain btcBlockChain;
        try {
            btcBlockChain = new BtcBlockChain(btcContext, btcBlockStore);
        } catch (BlockStoreException e) {
            throw new RuntimeException("Error initializing btc blockchain for tests");
        }
        int blocksToGenerate = Helper.randomInRange(minBtcBlocks, maxBtcBlocks);
        Helper.generateAndAddBlocks(btcBlockChain, blocksToGenerate);
    };
}
Also used : Context(co.rsk.bitcoinj.core.Context) Repository(org.ethereum.core.Repository) BlockStoreException(co.rsk.bitcoinj.store.BlockStoreException) BridgeStorageProvider(co.rsk.peg.BridgeStorageProvider) RepositoryBlockStore(co.rsk.peg.RepositoryBlockStore) BtcBlockStore(co.rsk.bitcoinj.store.BtcBlockStore) BtcBlockChain(co.rsk.bitcoinj.core.BtcBlockChain) RskSystemProperties(co.rsk.config.RskSystemProperties)

Example 10 with BlockStoreException

use of co.rsk.bitcoinj.store.BlockStoreException in project rskj by rsksmart.

the class ReceiveHeadersTest method receiveHeaders.

@Test
public void receiveHeaders() throws IOException {
    final int minBtcBlocks = 1000;
    final int maxBtcBlocks = 2000;
    BridgeStorageProviderInitializer storageInitializer = (BridgeStorageProvider provider, Repository repository, int executionIndex) -> {
        BtcBlockStore btcBlockStore = new RepositoryBlockStore(new RskSystemProperties(), repository, PrecompiledContracts.BRIDGE_ADDR);
        Context btcContext = new Context(networkParameters);
        BtcBlockChain btcBlockChain;
        try {
            btcBlockChain = new BtcBlockChain(btcContext, btcBlockStore);
        } catch (BlockStoreException e) {
            throw new RuntimeException("Error initializing btc blockchain for tests");
        }
        int blocksToGenerate = Helper.randomInRange(minBtcBlocks, maxBtcBlocks);
        BtcBlock lastBlock = Helper.generateAndAddBlocks(btcBlockChain, blocksToGenerate);
        blockToTry = Helper.generateBtcBlock(lastBlock);
    };
    ABIEncoder abiEncoder = (int executionIndex) -> {
        List<BtcBlock> headersToSendToBridge = new ArrayList<>();
        // Send just one header (that's the only case we're interested in measuring atm
        headersToSendToBridge.add(blockToTry);
        Object[] headersEncoded = headersToSendToBridge.stream().map(h -> h.bitcoinSerialize()).toArray();
        return Bridge.RECEIVE_HEADERS.encode(new Object[] { headersEncoded });
    };
    ExecutionStats stats = new ExecutionStats("receiveHeaders");
    executeAndAverage("receiveHeaders", 200, abiEncoder, storageInitializer, Helper.getZeroValueRandomSenderTxBuilder(), Helper.getRandomHeightProvider(10), stats);
    BridgePerformanceTest.addStats(stats);
}
Also used : Context(co.rsk.bitcoinj.core.Context) BlockStoreException(co.rsk.bitcoinj.store.BlockStoreException) BridgeStorageProvider(co.rsk.peg.BridgeStorageProvider) BtcBlockStore(co.rsk.bitcoinj.store.BtcBlockStore) BtcBlockChain(co.rsk.bitcoinj.core.BtcBlockChain) Repository(org.ethereum.core.Repository) RepositoryBlockStore(co.rsk.peg.RepositoryBlockStore) BtcBlock(co.rsk.bitcoinj.core.BtcBlock) ArrayList(java.util.ArrayList) List(java.util.List) RskSystemProperties(co.rsk.config.RskSystemProperties) Test(org.junit.Test)

Aggregations

BlockStoreException (co.rsk.bitcoinj.store.BlockStoreException)19 StoredBlock (co.rsk.bitcoinj.core.StoredBlock)9 Sha256Hash (co.rsk.bitcoinj.core.Sha256Hash)7 IOException (java.io.IOException)7 BigInteger (java.math.BigInteger)6 BtcBlockStore (co.rsk.bitcoinj.store.BtcBlockStore)5 BridgeStorageProvider (co.rsk.peg.BridgeStorageProvider)5 Repository (org.ethereum.core.Repository)5 VMException (org.ethereum.vm.exception.VMException)5 BtcBlock (co.rsk.bitcoinj.core.BtcBlock)4 VerificationException (co.rsk.bitcoinj.core.VerificationException)4 RskSystemProperties (co.rsk.config.RskSystemProperties)4 Test (org.junit.Test)4 AddressFormatException (co.rsk.bitcoinj.core.AddressFormatException)3 InsufficientMoneyException (co.rsk.bitcoinj.core.InsufficientMoneyException)3 UTXOProviderException (co.rsk.bitcoinj.core.UTXOProviderException)3 RepositoryBlockStore (co.rsk.peg.RepositoryBlockStore)3 PeginInstructionsException (co.rsk.peg.pegininstructions.PeginInstructionsException)3 ArrayList (java.util.ArrayList)3 BtcBlockChain (co.rsk.bitcoinj.core.BtcBlockChain)2