Les temps de verrouillage dans Bitcoin
Dans Bitcoin, il est possible de bloquer des fonds à un endroit jusqu’à une certaine date ou pour un temps donné grâce à des temps de verrouillage. Ces derniers sont utilisés dans les smart contracts d’applications diverses comme les canaux de paiement ou les échanges atomiques entre deux chaînes.
Il existe 4 types de temps de verrouillage. Les deux premiers sont nLocktime et nSequence, qui sont des champs spécifiques contenus dans les transactions. Les deux autres sont créés par les codes opératoires OP_CHECKLOCKTIMEVERIFY et OP_CHECKSEQUENCEVERIFY qui interviennent dans les scripts avancés.
Avertissement : Cet article est valide aussi bien pour Bitcoin (BTC) que pour Bitcoin Cash (BCH) et tous les protocoles apparentés à Bitcoin de manière générale.
nLocktime
nLocktime est un temps de verrouillage absolu qui est défini au niveau de la transaction.
Il s’agit du seul temps de verrouillage implémenté originellement par Satoshi Nakamoto. nLocktime est l’une des 4 éléments composant une transaction. Son fonctionnement est simple : la variable nLocktime est interprétée comme une date fixe et est comparée au temps du réseau Bitcoin. Si nLocktime désigne une date dans le futur, la transaction est marquée comme invalide et est rejetée ; elle ne sera valide qu’une fois la date passée.
Si 0 < nLocktime ≤ 500 000 000
, le temps de verrouillage est interprété comme une hauteur de bloc. Il est comparé à la hauteur du bloc dans laquelle la transaction est incluse. La comparaison est stricte : le bloc spécifié par nLocktime ne peut pas contenir la transaction, elle n’est valide que dans le bloc suivant. Cette interprétation autorise un temps de verrouillage jusqu’à presque 9500 ans dans le futur.
Si nLocktime > 500 000 000
, le temps de verrouillage est interprété comme un horodatage de l’Ère Unix, c’est-à-dire comme le nombre de secondes écoulées depuis le 1er janvier 1970 à 00:00 UTC. Celui-ci est comparé au temps du réseau appelé temps médian passé (de l’anglais Median Time Past noté MTP), qui est la médiane des horodatages des 11 derniers blocs minés. Ici aussi nLocktime doit être strictement plus grand que le temps de réseau. Comme nLocktime est encodé sur 4 octets, cette interprétation n’autorise les dates de verrouillage qu’à aller jusqu’en 2106.
Enfin, si nLocktime = 0
, le temps de verrouillage est désactivé. Même si la valeur de nLocktime est différente de zéro, il est également possible de le désactiver en fixant les numéros de séquence (nSequence) de toutes les entrées de la transaction à 0xffffffff
.
nSequence
Le numéro de séquence nSequence est un temps de verrouillage relatif qui est mis en place au niveau de la transaction, ou plutôt au niveau des entrées de la transaction.
Chaque entrée transactionnelle contient un numéro de séquence noté nSequence. À l’origine, Celui-ci n’est pas censé servir de temps de verrouillage, mais il a été redéfini à cet effet en 2016. Cette interprétation comme temps de verrouillage n’est effective que si le numéro de version (nVersion) de la transaction est supérieur (ou égal) à 2.
Comme nSequence est utilisé pour signaler certaines choses particulières comme la finalité d’une transaction (0xffffffff
), seuls 2 octets sur 4 sont exploités pour servir de temps de verrouillage.
Si le bit de désactivation est présent (nSequence > 0x80000000
), alors le temps de verrouillage relatif est désactivé.
Si le bit de type est fixé à 0, la valeur est interprétée comme un nombre de blocs, autorisant ainsi un temps de verrouillage maximal de 455 jours. S’il est fixé à 1, la valeur est interprétée comme un nombre d’unités de 512 secondes : 1 pour 512 secondes, 2 pour 1024 secondes, etc. Cette interprétation a été mise en place pour permettre des temps de verrouillage relatifs allant jusqu’à 388 jours.
Le temps de verrouillage est comparé à la période qui s’est écoulée depuis la date de la sortie transactionnelle précédente, c’est-à-dire le temps qu’ont passés les fonds au même endroit. Si le temps de verrouillage est supérieur à ce temps de blocage, alors la transaction est marquée comme invalide par les nœuds du réseau. Pour que le transaction soit valide, il faut donc que entrées de cette transaction aient un numéro de séquence strictement inférieur à la période de blocage des sorties qu’elles dépensent. Si l’une des comparaison n’est pas valide, alors la transaction entière n’est pas valide.
Les champs nLocktime et nSequence s’avèrent utiles dans certains cas d’usage, par exemple lorsque plusieurs personnes signent une même transaction. Cependant, leur potentiel est quelque peu limité. C’est pour cela que les développeurs du protocole ont mis en place de nouveaux codes opératoires dans le langage de script de Bitcoin permettant d’aller plus loin : OP_CHECKLOCKTIMEVERIFY et OP_CHECKSEQUENCEVERIFY.
CHECKLOCKTIMEVERIFY
Le code opératoire OP_CHECKLOCKTIMEVERIFY permet de verrouiller des fonds de manière absolue à l’intérieur d’un script. Il été ajouté au protocole fin 2015 par un soft fork.
OP_CHECKLOCKTIMEVERIFY compare l’élément placé au sommet de la pile de données au nLocktime de la transaction dans laquelle est exécuté le script. Si cet élément est strictement plus petit que nLocktime, la transaction est invalide. Il faut donc que le nLocktime de la transaction soit au moins égal à cet élément.
L’effet produit est qu’un script de verrouillage contenant OP_CHECKLOCKTIMEVERIFY verrouille les fonds pour jusqu’à la date indiquée. Le script valide le plus simple qu’on puisse construire (anyone-can-spend) avec ce code opératoire est :
<temps de verrouillage absolu> CHECKLOCKTIMEVERIFY DROP
Le temps de verrouillage présent dans le script est interprété comme nLocktime : comme une hauteur de bloc ou comme un horodatage Unix. Puisque les nombres des scripts sont signés (ils peuvent être négatifs), il est possible que ce temps soit encodé sur 5 octets.
Les détails de l’exécution de ce code opératoire sont données dans le BIP-65. À l’exécution, le script est invalidé si l’une des conditions suivantes est vérifiée :
- La pile est vide ;
- L’élément au sommet de la pile est strictement négatif ;
- Le type (hauteur ou horodatage) du temps de verrouillage au sommet de la pile et celui du champ nLocktime ne sont pas les mêmes ;
- L’élément au sommet de la pile est strictement plus grand que le champ nLocktime de la transaction ;
- Le champ nSequence de l’entrée de la transaction est
0xffffffff
.
Pour illustrer la chose, on peut supposer que quelqu’un veuille bloquer des fonds à une adresse jusqu’au 1er janvier 2020 00:00:00 UTC. L’horodatage correspondant est 1577836800
. La personne se retrouve donc à envoyer des fonds au contrat suivant :
<1577836800> CHECKLOCKTIMEVERIFY DROP <clé publique> CHECKSIG
Bien évidemment, l’utilisateur possède la clé privée correspondant à la clé publique présente. Pour déverrouiller les fonds, il devra, à l’aide de cette clé privée, signer une transaction après le 1er janvier 2020 à minuit contenant un nLocktime au moins égal à 1577836800
.
CHECKSEQUENCEVERIFY
À l’instar de OP_CHECKLOCKTIMEVERIFY, le code opératoire OP_CHECKSEQUENCEVERIFY permet de verrouiller des fonds de manière relative à l’intérieur d’un script. Il s’agit d’une innovation qui a été ajoutée en même temps que la nouvelle interprétation de nSequence et que le temps médian passé en 2016, là aussi par un soft fork.
OP_CHECKSEQUENCEVERIFY compare l’élément au sommet de la pile au nSequence de l’entrée transactionnelle qui exécute le script. Si l’élément est strictement plus grand que le nSequence correspondant, alors la transaction est invalide. On doit donc avoir un nSequence au moins égal à cet élément.
Ce code opératoire permet donc de verrouiller des fonds pour un temps donné à partir d’un script. Le script le plus simple (anyone-can-spend) contenant OP_CHECKSEQUENCEVERIFY est :
<temps de verrouillage relatif> CHECKSEQUENCEVERIFY DROP
Ici le temps de verrouillage relatif est interprété comme un champ nSequence. Il est toujours encodé sur 3 octets.
Le BIP-68 explique en détail comment est exécuté ce code opératoire. À l’exécution, le script est invalidé si l’une des conditions suivantes est vérifiée :
- La pile est vide ;
- L’élément au sommet de la pile est strictement négatif ;
- Le bit de désactivation (1 << 31) de l’élément au sommet de la pile est fixé à 0, et :
- La version de la transaction est strictement inférieure à 2 ;
- Le bit de désactivation (1 << 31) du numéro de séquence de l’entrée transactionnelle est égal à 1 ;
- Les types des temps de verrouillage relatifs ne sont pas les mêmes ;
- L’élément au sommet de la pile est strictement plus grand que la numéro de séquence de l’entrée transactionnelle (quand masqué selon le BIP-68).
À des fins d’illustration, on imagine qu’un utilisateur veuille répartir des fonds pour les verrouiller pendant deux périodes différentes : l’une de 288 blocs, soit environ 2 jours ; l’autre de 86528 secondes, ce qui fait 1 jour.
Les contrats respectifs sont :
<288> CHECKSEQUENCEVERIFY DROP <clé publique> CHECKSIG
<0x4000a9> CHECKSEQUENCEVERIFY DROP <clé publique> CHECKSIG
Cet utilisateur commence par bloquer des fonds à la première adresse pour 288 blocs. Puis le lendemain vers la même heure, il bloque le reste des fonds à la deuxième adresse pour 1 jour. Ce n’est donc qu’à partir du surlendemain qu’il peut déverouiller tous les fonds (à peu près au même moment). Pour ce faire, il construit une transaction à deux entrées contenant chacune le numéro de séquence adéquat.
On peut placer les différentes méthodes de verrouillage temporel dans le tableau récapitulatif suivant :
Absolu | Relatif | |
---|---|---|
Transaction | ||
Script |
Ainsi, il est possible de verrouiller des fonds pour un temps donné dans Bitcoin, et ceci peut se faire de multiples manières. Si seulement nLocktime était présent au début, Bitcoin a pu évoluer pour offrir plus de possiblités. Aujourd’hui, ces temps de verrouillage sont présents dans des applications comme les canaux de paiement du Lightning Network, mais il ne fait aucun doute que leur potentiel est bien plus grand et qu’ils seront utilisés pour bien plus de choses à l’avenir.
Références
James Prestwich, Bitcoin’s Time Locks, 13 octobre 2017.
BIP-65: OP_CHECKLOCKTIMEVERIFY
BIP-68: Relative lock-time using consensus-enforced sequence numbers
BIP-112: OP_CHECKSEQUENCEVERIFY
BIP-113: Median time-past as endpoint for lock-time calculations