Search in sources :

Example 1 with ScriptChunk

use of co.rsk.bitcoinj.script.ScriptChunk 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 ScriptChunk

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

the class BridgeUtils method countMissingSignatures.

/**
 * Return the amount of missing signatures for a tx.
 * @param btcContext Btc context
 * @param btcTx The btc tx to check
 * @return 0 if was signed by the required number of federators, amount of missing signatures otherwise
 */
public static int countMissingSignatures(Context btcContext, BtcTransaction btcTx) {
    // Check missing signatures for only one input as it is not
    // possible for a federator to leave unsigned inputs in a tx
    Context.propagate(btcContext);
    int unsigned = 0;
    TransactionInput input = btcTx.getInput(0);
    Script scriptSig = input.getScriptSig();
    List<ScriptChunk> chunks = scriptSig.getChunks();
    Script redeemScript = new Script(chunks.get(chunks.size() - 1).data);
    RedeemScriptParser parser = RedeemScriptParserFactory.get(redeemScript.getChunks());
    MultiSigType multiSigType;
    int lastChunk;
    multiSigType = parser.getMultiSigType();
    if (multiSigType == MultiSigType.STANDARD_MULTISIG || multiSigType == MultiSigType.FAST_BRIDGE_MULTISIG) {
        lastChunk = chunks.size() - 1;
    } else {
        lastChunk = chunks.size() - 2;
    }
    for (int i = 1; i < lastChunk; i++) {
        ScriptChunk chunk = chunks.get(i);
        if (!chunk.isOpCode() && chunk.data.length == 0) {
            unsigned++;
        }
    }
    return unsigned;
}
Also used : Script(co.rsk.bitcoinj.script.Script) MultiSigType(co.rsk.bitcoinj.script.RedeemScriptParser.MultiSigType) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk) RedeemScriptParser(co.rsk.bitcoinj.script.RedeemScriptParser)

Example 3 with ScriptChunk

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

the class BridgeUtils method scriptCorrectlySpendsTx.

public static boolean scriptCorrectlySpendsTx(BtcTransaction tx, int index, Script script) {
    try {
        TransactionInput txInput = tx.getInput(index);
        // Check the input does not contain script op codes
        List<ScriptChunk> chunks = txInput.getScriptSig().getChunks();
        Iterator it = chunks.iterator();
        while (it.hasNext()) {
            ScriptChunk chunk = (ScriptChunk) it.next();
            if (chunk.isOpCode() && chunk.opcode > 96) {
                return false;
            }
        }
        txInput.getScriptSig().correctlySpends(tx, index, script, Script.ALL_VERIFY_FLAGS);
        return true;
    } catch (ScriptException se) {
        return false;
    }
}
Also used : ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk)

Example 4 with ScriptChunk

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

the class BridgeUtils method extractRedeemScriptFromInput.

private static Optional<Script> extractRedeemScriptFromInput(TransactionInput txInput) {
    Script inputScript = txInput.getScriptSig();
    List<ScriptChunk> chunks = inputScript.getChunks();
    if (chunks == null || chunks.isEmpty()) {
        return Optional.empty();
    }
    byte[] program = chunks.get(chunks.size() - 1).data;
    if (program == null) {
        return Optional.empty();
    }
    try {
        Script redeemScript = new Script(program);
        return Optional.of(redeemScript);
    } catch (ScriptException e) {
        logger.debug("[extractRedeemScriptFromInput] Failed to extract redeem script from tx input {}. {}", txInput, e.getMessage());
        return Optional.empty();
    }
}
Also used : Script(co.rsk.bitcoinj.script.Script) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk)

Example 5 with ScriptChunk

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

the class BridgeSupportTest method addSignatureCreateEventLog.

@Test
public void addSignatureCreateEventLog() throws Exception {
    // Setup
    Federation federation = bridgeConstants.getGenesisFederation();
    Repository track = createRepository().startTracking();
    BridgeStorageProvider provider = new BridgeStorageProvider(track, PrecompiledContracts.BRIDGE_ADDR, bridgeConstants, activationsBeforeForks);
    // Build prev btc tx
    BtcTransaction prevTx = new BtcTransaction(btcParams);
    TransactionOutput prevOut = new TransactionOutput(btcParams, prevTx, Coin.FIFTY_COINS, federation.getAddress());
    prevTx.addOutput(prevOut);
    // Build btc tx to be signed
    BtcTransaction btcTx = new BtcTransaction(btcParams);
    btcTx.addInput(prevOut).setScriptSig(createBaseInputScriptThatSpendsFromTheFederation(federation));
    TransactionOutput output = new TransactionOutput(btcParams, btcTx, Coin.COIN, new BtcECKey().toAddress(btcParams));
    btcTx.addOutput(output);
    // Save btc tx to be signed
    final Keccak256 rskTxHash = PegTestUtils.createHash3(1);
    provider.getRskTxsWaitingForSignatures().put(rskTxHash, btcTx);
    provider.save();
    track.commit();
    // Setup BridgeSupport
    BridgeEventLogger eventLogger = mock(BridgeEventLogger.class);
    BridgeSupport bridgeSupport = getBridgeSupport(bridgeConstants, provider, track, eventLogger, mock(Block.class), null);
    // Create signed hash of Btc tx
    Script inputScript = btcTx.getInputs().get(0).getScriptSig();
    List<ScriptChunk> chunks = inputScript.getChunks();
    byte[] program = chunks.get(chunks.size() - 1).data;
    Script redeemScript = new Script(program);
    Sha256Hash sigHash = btcTx.hashForSignature(0, redeemScript, BtcTransaction.SigHash.ALL, false);
    BtcECKey privateKeyToSignWith = BridgeRegTestConstants.REGTEST_FEDERATION_PRIVATE_KEYS.get(0);
    BtcECKey.ECDSASignature sig = privateKeyToSignWith.sign(sigHash);
    List derEncodedSigs = Collections.singletonList(sig.encodeToDER());
    BtcECKey federatorPubKey = findPublicKeySignedBy(federation.getBtcPublicKeys(), privateKeyToSignWith);
    bridgeSupport.addSignature(federatorPubKey, derEncodedSigs, rskTxHash.getBytes());
    verify(eventLogger, times(1)).logAddSignature(federatorPubKey, btcTx, rskTxHash.getBytes());
}
Also used : Script(co.rsk.bitcoinj.script.Script) Keccak256(co.rsk.crypto.Keccak256) ScriptChunk(co.rsk.bitcoinj.script.ScriptChunk) Repository(org.ethereum.core.Repository) MutableRepository(org.ethereum.db.MutableRepository) PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation(co.rsk.peg.PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation) PegTestUtils.createBaseRedeemScriptThatSpendsFromTheFederation(co.rsk.peg.PegTestUtils.createBaseRedeemScriptThatSpendsFromTheFederation) Block(org.ethereum.core.Block) RLPList(org.ethereum.util.RLPList) ActivationConfigsForTest(org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest) Test(org.junit.Test)

Aggregations

ScriptChunk (co.rsk.bitcoinj.script.ScriptChunk)17 Script (co.rsk.bitcoinj.script.Script)14 TransactionSignature (co.rsk.bitcoinj.crypto.TransactionSignature)7 Keccak256 (co.rsk.crypto.Keccak256)5 ActivationConfigsForTest (org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest)5 Test (org.junit.Test)5 BtcECKey (co.rsk.bitcoinj.core.BtcECKey)4 Sha256Hash (co.rsk.bitcoinj.core.Sha256Hash)4 TransactionInput (co.rsk.bitcoinj.core.TransactionInput)4 BtcTransaction (co.rsk.bitcoinj.core.BtcTransaction)3 TransactionOutPoint (co.rsk.bitcoinj.core.TransactionOutPoint)3 PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation (co.rsk.peg.PegTestUtils.createBaseInputScriptThatSpendsFromTheFederation)3 PegTestUtils.createBaseRedeemScriptThatSpendsFromTheFederation (co.rsk.peg.PegTestUtils.createBaseRedeemScriptThatSpendsFromTheFederation)3 Block (org.ethereum.core.Block)3 Repository (org.ethereum.core.Repository)3 MutableRepository (org.ethereum.db.MutableRepository)3 RLPList (org.ethereum.util.RLPList)3 RedeemScriptParser (co.rsk.bitcoinj.script.RedeemScriptParser)2 MultiSigType (co.rsk.bitcoinj.script.RedeemScriptParser.MultiSigType)2 ActivationConfig (org.ethereum.config.blockchain.upgrades.ActivationConfig)2