Direct Integration with Bitcoin

So there is a risk of losing keys forever no matter how small the chances of it happening are

They are not really talking about DKG / threshold signatures, but rather about BLS signatures and the bilinear pairings with gap Diffie–Hellman assumptions underlying it. As Dom pointed out in the video, BLS and pairings stem from 2001 (https://www.iacr.org/archive/asiacrypt2001/22480516.pdf), and already in 2016 bilinear pairings were widely used in cryptography. Today (as @Maxfinity also points) out Ethereum’s beacon chain will use BLS signatures and many other blockchain projects do too.

2 Likes

I think we’d have major issues across the board if the system subnet (NNS) had a catastrophic failure like that.

Of course NNS is the single point of failure for the whole computer. My question was if it was possible or not.

Also can someone please tell me how many nodes of the subnets have to be malicious to do any harm in this context. And, when is DFINITY planning to finish developing the Periodic node shuffling feature

2 Likes

The IC is generally built on the assumption of 2/3 of the nodes being legitimate. From what I gathered in discussions a malicious amount of >1/3 but <2/3 can only stall the subnet, to be actively destructive there would have to be >2/3 malicious nodes. For comparison: in BTC, the threshold for both (stalling and destructive action) is at 1/2.

2 Likes

Understood. I can’t think of a single system that has managed to 100% eliminate the risk of losing a key. From a key management perspective I’m pretty impressed with what they’re doing. It seems pretty resilient and will only get better with time.

1 Like

Is this the same for the ECDSA subnet? IIRC there was a community conversation last year where one of the cryptographers at Dfinity explained that only 1/3 malicious nodes were needed, and that is why the subnet was going to have a node count equal to that of the system subnet.

Edit: It was Victor Shoup; here is a link to his presentation. There may be more explanation during the Q&A at the end but I couldn’t find it. https://youtu.be/MulbKPwv6_s?list=PLuhDt1vhGcrez-f3I0_hvbwGZHZzkZ7Ng&t=1237

Edit 2: I just realized there is an updated ECDSA Threshold video from last month. In this one it sounds like the threshold is higher than was originally planned. I would definitely appreciate any clarification you can provide @dieter.sommer https://youtu.be/stHaou90XiM?t=958

Also, thank you for uploading the latest video. I think these are wonderful resources for those of us who are more visual learners.

2 Likes

DFINITY is focussing on delivering SNS and BTC integration at the moment. Afaik, no design or implementation plan for automatic periodic reshuffling of nodes exists yet.

1 Like

Good point, I’m not actually sure about that. I’ll let @dieter.sommer answer that as I’m now too far in uncertain territory :smile:

2 Likes

This also reflects my knowledge about this. There have been discussions on node rotation, but no concrete design for it yet.

In every system you have a residual risk of a secret key getting lost. One can only reduce it to an as-low level as reasonably possible. With our approach of having the key secret shared on two large subnets, I think we have pretty good resilience against disasters.

5 Likes

Update

The Bitcoin feature is code complete from a replica perspective for the upcoming Beta / Chromium release. We are currently working on finishing an example dApp to showcase the feature and the feature documentation.

Threshold ECSDA, a crucial dependency for the Bitcoin integration, is about to be completed for the Beta / Chromium release as well.

Things are nearing completion, we will keep you posted on progress!

Stay tuned! :slight_smile:

Edit: Just to clarify things, with “Bitcoin feature” above I meant the technical integration of the IC stack with the Bitcoin network, i.e., ingesting Bitcoin blocks, validating them, processing the UTXOs contained therein, and serving UTXOs to canisters as well as submitting Bitcoin transactions to the Bitcoin network. Threshold ECDSAhas been considered as a separate feature in that post that is also very close to being completed.
Sometimes, when we speak of “Bitcoin integration” or the Bitcoin feature" in public, we refer to the union of this technical integration of the IC stack with Bitcoin and threshold ECDSA. Just to clarify so that everyone understands the same thing here.

30 Likes

And to add to @dieter.sommer’s announcement, here’s an updated demo that showcases the various Bitcoin APIs.

The upgraded demo supports the following endpoints:

  • get_balance to get the balance of a Bitcoin address.
  • get_utxos to get the UTXOs of a Bitcoin address, including pagination support for addresses that have a very large number of UTXOs.
  • get_current_fee_percentiles to get the 100 fee percentiles over the last 10k transactions for fee estimates.
  • send_transaction for sending signed transactions to the bitcoin network.

This demo is a very bare-bones demo to demonstrate the raw API. A more fledged demo that showcases how canisters can generate bitcoin addresses and sign transactions will, as @dieter.sommer already mentioned, be released imminently,

18 Likes

Any news about the date ?

6 Likes

Is it possible to get the status of transactions given their txid? We find it quite limiting that only “unspent” transaction outputs can be queried. It is equally important to know if some previous outgoing transactions from a canister are indeed included in the blockchain (whether they are spent or unspent is irrelevant, but if they are later spent by the receiver, they can no longer be queried through get_utxos).

The current management canister endpoints related to Bitcoin don’t make it possible to get for sure the status of any arbitrary transaction given their transaction ids (more precisely those that your canister haven’t sent). However when using the bitcoin_send_transaction endpoint, it means that you know the transaction (UTXOs used etc… and thus the transaction id). So you are able

to know [for sure] if some previous outgoing transactions from [your] canister are indeed included in the blockchain

Indeed by using bitcoin_get_utxos you can retrieve the UTXOs that you still own according to the provided number of confirmations, after having sent your transaction that will consume some of them once it got included in the blockchain.
As UTXOs are uniquely identified by their transaction id and index of the transaction output, because your canister is the only entity being able to sign transactions using the UTXOs held by your canister and because Bitcoin transactions are atomic, if none of the inputs were used in another broadcasted transaction by using bitcoin_send_transaction and if you haven’t updated your transaction by using BIP-125 Replace-by-fee, then if you still hold any UTXOs spent, according to a given number of confirmations, by the just sent transaction, it means that the transaction isn’t yet included in the blockchain, according to this number of confirmations.

So there isn’t any need to retrieve the UTXOs of any recipient address.

Example:

I. Your canister holds two UTXOs on the Bitcoin address maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa (you can call bitcoin_get_utxos to retrieve this information):

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the to-be-used transaction output: 0
    Value: 1 BTC
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the to-be-used transaction output: 0
    Value: 2 BTC

II. Your canister builds the following transaction (of id 3333333333333333333333333333333333333333333333333333333333333333), which sends 2.5 BTC to mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb and sends this transaction to the Bitcoin network by using the bitcoin_send_transaction endpoint:

Inputs:

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the transaction output: 0
    Value: 1 BTC
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the transaction output: 0
    Value: 2 BTC

Outputs:

  1. Address: mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    Value: 2.5 BTC
  2. Address: maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    Value: 0.49 BTC

III. If afterwards you call bitcoin_get_utxos, the result can either be:

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the transaction output: 0
    Value: 1 BTC
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the transaction output: 0
    Value: 2 BTC

or:

  • Transaction id: 3333333333333333333333333333333333333333333333333333333333333333
    Index of the to-be-used transaction output: 1
    Value: 0.49 BTC

According to the passed number of confirmations to bitcoin_get_utxos:

  • In the first case, it means that your transaction isn’t confirmed yet.
  • In the second case, it means that your transaction got confirmed.
3 Likes

Great explanation! However, when the sender itself isn’t one of the receivers, it is out of luck. It could mean that we just always have to keep some dust in an address even if it is supposed to be of one time use. Not the end of world of course.

1 Like

However, when the sender itself isn’t one of the receivers, it is out of luck.

As far as I know, the case when the sender itself isn’t one of the receivers isn’t a problem.
In the previous example we haven’t used the last bitcoin_get_utxos call to look for the generated UTXO our canister received. We were looking for any input of transaction spending:

Just checking whether or not any of these two UTXOs:

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the transaction output: 0
    Value: 1 BTC
    or
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the transaction output: 0
    Value: 2 BTC

were spent and thus not part of your canister’s UTXO set.

This sample pseudo-Motoko code may make it clearer:

type TransactionId = Text;

/// An unspent transaction output representation.
type Utxo = {
    transaction_id : TransactionId;
    transaction_output_number : Nat32;
};

/// A Bitcoin transaction representation.
class Transaction(...) {
    // These UTXOs will be used as the inputs.
    public let utxos : [Utxo] = // ...

    /// Returns the transaction as a byte array.
    public func toBytes() : [Nat8] {
        // ...
    };

    /// Returns the transaction id.
    public func id() : TransactionId {
        // ...
    };
};

/// Cache of pending transactions that get cleaned automatically by using `is_transaction_pending`.
let pending_transactions : HashMap.HashMap<TransactionId, Utxo> = HashMap.HashMap(0, Text.equal, Text.hash);

/// Sends a Bitcoin transaction to the Bitcoin network and adds it to the pending transactions cache.
func send_transaction() : async TransactionId {
    // ...
    // Assumes having built a new `transaction`.
    // "new" means that none of the inputs were used in another broadcasted transaction.
    let transaction_bytes : [Nat8] = transaction.toBytes();
    // Sends the Bitcoin transaction to the Bitcoin network.
    await management_canister_actor.bitcoin_send_transaction(transaction_bytes);
    // ...
    // Gets the id of the transaction.
    let transaction_id : TransactionId = transaction.id();
    // We only need to check later on if the first input got spent in order to know whether or not the whole transaction was confirmed.
    let first_input = transaction.utxos[0];
    // Adds "the sent Bitcoin transaction" to the pending transactions cache.
    pending_transactions.put(transaction_id, first_input);
    transaction_id
};

/// Returns true if the sent transaction is already included in the blockchain according to the provided `minimum_of_confirmations`, otherwise false.
/// This function assumes you passed a `transaction_id` that got obtained by using `send_transaction`.
func is_transaction_confirmed(transaction_id : TransactionId, minimum_of_confirmations : Nat32) : async Bool {
    let utxo_option : Option<Utxo> = pending_transactions.get(transaction_id);
    switch (utxo_option) {
        case (?utxo) {
            // We don't use the possibly generated UTXO back to the Bitcoin canister address.
            // We are only looking for spending of pending spending UTXOs.
            let is_utxo_spent = await is_still_owning_utxo(utxo, minimum_of_confirmations);
            if(is_utxo_spent) {
                pending_transactions.delete(transaction_id);
            };
            is_utxo_spent
        };
        case null {
            true
        };
    }
};

/// Returns true if the canister is still holding the provided `utxo` at a depth of `minimum_of_confirmations`.
func is_still_owning_utxo(utxo : Utxo, minimum_of_confirmations : Nat32) : async Bool {
    let utxos = await management_canister_actor.bitcoin_get_utxos(bitcoin_canister_address, minimum_of_confirmations);
    Option.isSome(Array.find(utxos, func(haystack_utxo) = { haystack_utxo == utxo })))
};

Otherwise here is another example.

Example:

I. Your canister holds two UTXOs on the Bitcoin address maaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa (you can call bitcoin_get_utxos to retrieve this information):

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the to-be-used transaction output: 0
    Value: 1 BTC
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the to-be-used transaction output: 0
    Value: 2 BTC

II. Your canister builds the following transaction (of id 3333333333333333333333333333333333333333333333333333333333333333), which sends 2.9 BTC to mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb and sends this transaction to the Bitcoin network by using the bitcoin_send_transaction endpoint:

Inputs:

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the transaction output: 0
    Value: 1 BTC
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the transaction output: 0
    Value: 2 BTC

Outputs:

  1. Address: mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    Value: 2.9 BTC

    0.1 BTC goes to the miner.

III. If afterwards you call bitcoin_get_utxos, the result can either be:

  • Transaction id: 0000000000000000000000000000000000000000000000000000000000000000
    Index of the transaction output: 0
    Value: 1 BTC
  • Transaction id: 1111111111111111111111111111111111111111111111111111111111111111
    Index of the transaction output: 0
    Value: 2 BTC

or:

(nothing)

According to the passed number of confirmations to bitcoin_get_utxos:

  • In the first case, it means that your transaction isn’t confirmed yet.
  • In the second case, it means that your transaction got confirmed.
1 Like

That is not always the case. They may disappear from your canister’s UTXO set because the blockchain has re-organized.

By the blockchain re-organizing, you mean a fork overtaking the current tip? As long as you’re spending UTXOs that have a sufficiently high number of confirmations, the probability of them being removed from the chain due to a fork falls to practically zero, no?

2 Likes

That of course works, but is perhaps not the most efficient way of building on bitcoin chain. You should be able to continue building transactions/blocks on top of the “current longest”, and when that changes, you build on the “new longest”, without having to wait for for high number of confirmations. It could mean that a number of previous transactions you sent were not included in the “new longest”, but you should always deal with that anyway, they do not pose security risk in general (since double spending is not possible by construction).

1 Like