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.
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
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.
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.
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.
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.
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
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.
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!
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.
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,
Any news about the date ?
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:
- Address:
mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Value:2.5
BTC - 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.
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.
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:
-
Address:
mbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Value:2.9
BTC0.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.
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?
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).