Search in sources :

Example 16 with Wallet

use of co.rsk.bitcoinj.wallet.Wallet in project rskj by rsksmart.

the class BridgeUtilsTest method getGenesisFederationForTest.

private Federation getGenesisFederationForTest(BridgeConstants bridgeConstants, Context btcContext) {
    Federation federation = bridgeConstants.getGenesisFederation();
    Wallet wallet = new BridgeBtcWallet(btcContext, Collections.singletonList(federation));
    Address federationAddress = federation.getAddress();
    wallet.addWatchedAddress(federationAddress, federation.getCreationTime().toEpochMilli());
    return federation;
}
Also used : Address(co.rsk.bitcoinj.core.Address) RskAddress(co.rsk.core.RskAddress) Wallet(co.rsk.bitcoinj.wallet.Wallet)

Example 17 with Wallet

use of co.rsk.bitcoinj.wallet.Wallet 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 18 with Wallet

use of co.rsk.bitcoinj.wallet.Wallet in project rskj by rsksmart.

the class BridgeUtils method getFederationsSpendWallet.

public static Wallet getFederationsSpendWallet(Context btcContext, List<Federation> federations, List<UTXO> utxos) {
    Wallet wallet = new BridgeBtcWallet(btcContext, federations);
    RskUTXOProvider utxoProvider = new RskUTXOProvider(btcContext.getParams(), utxos);
    wallet.setUTXOProvider(utxoProvider);
    federations.stream().forEach(federation -> {
        wallet.addWatchedAddress(federation.getAddress(), federation.getCreationTime().toEpochMilli());
    });
    wallet.setCoinSelector(new RskAllowUnconfirmedCoinSelector());
    return wallet;
}
Also used : Wallet(co.rsk.bitcoinj.wallet.Wallet) RskAllowUnconfirmedCoinSelector(co.rsk.peg.bitcoin.RskAllowUnconfirmedCoinSelector)

Example 19 with Wallet

use of co.rsk.bitcoinj.wallet.Wallet in project rskj by rsksmart.

the class BridgeSupportTest method getActiveFederationWallet.

@Test
public void getActiveFederationWallet() throws IOException {
    Federation expectedFederation = new Federation(Arrays.asList(new BtcECKey[] { BtcECKey.fromPublicOnly(Hex.decode("036bb9eab797eadc8b697f0e82a01d01cabbfaaca37e5bafc06fdc6fdd38af894a")), BtcECKey.fromPublicOnly(Hex.decode("031da807c71c2f303b7f409dd2605b297ac494a563be3b9ca5f52d95a43d183cc5")) }), Instant.ofEpochMilli(5005L), 0L, NetworkParameters.fromID(NetworkParameters.ID_REGTEST));
    BridgeSupport bridgeSupport = getBridgeSupportWithMocksForFederationTests(false, expectedFederation, null, null, null, null, null);
    Context expectedContext = mock(Context.class);
    Whitebox.setInternalState(bridgeSupport, "btcContext", expectedContext);
    BridgeStorageProvider provider = (BridgeStorageProvider) Whitebox.getInternalState(bridgeSupport, "provider");
    Object expectedUtxos = provider.getNewFederationBtcUTXOs();
    final Wallet expectedWallet = mock(Wallet.class);
    PowerMockito.mockStatic(BridgeUtils.class);
    PowerMockito.when(BridgeUtils.getFederationSpendWallet(any(), any(), any())).then((InvocationOnMock m) -> {
        Assert.assertEquals(m.getArgumentAt(0, Context.class), expectedContext);
        Assert.assertEquals(m.getArgumentAt(1, Federation.class), expectedFederation);
        Assert.assertEquals(m.getArgumentAt(2, Object.class), expectedUtxos);
        return expectedWallet;
    });
    Assert.assertSame(expectedWallet, bridgeSupport.getActiveFederationWallet());
}
Also used : Wallet(co.rsk.bitcoinj.wallet.Wallet) SimpleWallet(co.rsk.peg.simples.SimpleWallet) InvocationOnMock(org.mockito.invocation.InvocationOnMock) PrepareForTest(org.powermock.core.classloader.annotations.PrepareForTest) Test(org.junit.Test)

Example 20 with Wallet

use of co.rsk.bitcoinj.wallet.Wallet in project rskj by rsksmart.

the class BridgeUtilsTest method getFederationSpendWallet.

@Test
public void getFederationSpendWallet() throws UTXOProviderException {
    NetworkParameters regTestParameters = NetworkParameters.fromID(NetworkParameters.ID_REGTEST);
    Federation federation = new Federation(Arrays.asList(new BtcECKey[] { BtcECKey.fromPublicOnly(Hex.decode("036bb9eab797eadc8b697f0e82a01d01cabbfaaca37e5bafc06fdc6fdd38af894a")), BtcECKey.fromPublicOnly(Hex.decode("031da807c71c2f303b7f409dd2605b297ac494a563be3b9ca5f52d95a43d183cc5")) }), Instant.ofEpochMilli(5005L), 0L, regTestParameters);
    Context mockedBtcContext = mock(Context.class);
    when(mockedBtcContext.getParams()).thenReturn(regTestParameters);
    List<UTXO> mockedUtxos = new ArrayList<>();
    mockedUtxos.add(mock(UTXO.class));
    mockedUtxos.add(mock(UTXO.class));
    mockedUtxos.add(mock(UTXO.class));
    Wallet wallet = BridgeUtils.getFederationSpendWallet(mockedBtcContext, federation, mockedUtxos);
    Assert.assertEquals(BridgeBtcWallet.class, wallet.getClass());
    assertIsWatching(federation.getAddress(), wallet, regTestParameters);
    CoinSelector selector = wallet.getCoinSelector();
    Assert.assertEquals(RskAllowUnconfirmedCoinSelector.class, selector.getClass());
    UTXOProvider utxoProvider = wallet.getUTXOProvider();
    Assert.assertEquals(RskUTXOProvider.class, utxoProvider.getClass());
    Assert.assertEquals(mockedUtxos, utxoProvider.getOpenTransactionOutputs(Collections.emptyList()));
}
Also used : Wallet(co.rsk.bitcoinj.wallet.Wallet) ArrayList(java.util.ArrayList) CoinSelector(co.rsk.bitcoinj.wallet.CoinSelector) RskAllowUnconfirmedCoinSelector(co.rsk.peg.bitcoin.RskAllowUnconfirmedCoinSelector) Test(org.junit.Test)

Aggregations

Wallet (co.rsk.bitcoinj.wallet.Wallet)29 Test (org.junit.Test)11 Script (co.rsk.bitcoinj.script.Script)8 RskAddress (co.rsk.core.RskAddress)7 RskAllowUnconfirmedCoinSelector (co.rsk.peg.bitcoin.RskAllowUnconfirmedCoinSelector)7 Keccak256 (co.rsk.crypto.Keccak256)6 Context (co.rsk.bitcoinj.core.Context)5 SendRequest (co.rsk.bitcoinj.wallet.SendRequest)5 BridgeConstants (co.rsk.config.BridgeConstants)5 IOException (java.io.IOException)5 Block (org.ethereum.core.Block)5 Repository (org.ethereum.core.Repository)5 Address (co.rsk.bitcoinj.core.Address)4 BtcTransaction (co.rsk.bitcoinj.core.BtcTransaction)4 Coin (co.rsk.bitcoinj.core.Coin)4 TransactionSignature (co.rsk.bitcoinj.crypto.TransactionSignature)4 ScriptBuilder (co.rsk.bitcoinj.script.ScriptBuilder)4 ScriptChunk (co.rsk.bitcoinj.script.ScriptChunk)4 BlockStoreException (co.rsk.bitcoinj.store.BlockStoreException)4 PanicProcessor (co.rsk.panic.PanicProcessor)4