Publication, Normalisation, Vérification et Diffusion des Transactions sur les Nœuds ADAMANT
Fonctions
Transaction.prototype.verify vérifie une transaction récupérée depuis d’autres blocs, qui peut inclure des transactions anciennes. Transactions.prototype.publish vérifie les transactions entrantes provenant des utilisateurs, en validant les transactions non confirmées récentes. verify est également appelée après réception d’une nouvelle transaction via l’API, suite aux méthodes publish et objectNormalize. publish n’est appelée que lors de l’envoi de transactions via l’API publique.
Traitement des Transactions
Lorsqu’un nœud reçoit une nouvelle transaction depuis une application cliente via l’API en tant que premier nœud à la voir, Logic->Transaction.prototype.publish crée une nouvelle transaction à partir des données reçues, valide les propriétés de la transaction cliente (y compris timestampMs), ajoute un ID de transaction, puis traite la transaction non confirmée comme d’habitude. Si un nœud reçoit une nouvelle transaction depuis un autre pair à la hauteur actuelle, il valide la transaction et supprime le pair si elle est invalide, puis traite la transaction non confirmée. Lors de la récupération d’une transaction depuis un autre pair lors de la montée en puissance ou de la vérification de la blockchain depuis la hauteur 0, le nœud valide la transaction, supprime le pair si elle est invalide, et traite la transaction non confirmée tout en vérifiant plusieurs transactions simultanément. La validation consiste pour le nœud à appeler objectNormalize() pour la validation du schéma de la transaction reçue.
Le flux de traitement des transactions non confirmées commence par logic.transaction.process(), qui vérifie l’ID de transaction et normalise l’ID de l’expéditeur. Ensuite, la transaction est normalisée : logic.transaction.objectNormalize() valide l’objet transaction par rapport à un schéma et supprime les propriétés inutiles, y compris timestampMs avant l’activation de spaceship. Enfin, logic.transaction.verify() est appelée pour vérifier toutes les propriétés telles que timestamp, timestampMs et signature.
Supprimer un pair signifie le retirer de la liste des pairs jusqu’à ce qu’il soit redécouvert ; les nœuds n’interdisent pas les pairs, donc le statut BANNED n’est jamais appliqué. Un nœud diffuse une transaction vers d’autres nœuds après avoir traité la transaction non confirmée. Outre objectNormalize(), le nœud possède normalize(), utilisée uniquement pour le point de terminaison POST /transactions/normalize qui devrait être déprécié. Le nœud dispose à la fois de apply() et applyUnconfirmed() car les méthodes “apply” modifient les soldes des comptes de l’expéditeur et du destinataire, tandis que applyUnconfirmed() modifie le solde non confirmé ; il en va de même pour les méthodes “undo”. Lorsqu’un nœud génère un nouveau bloc, les transactions sont extraites d’une liste déjà vérifiée. Actuellement, publish() vérifie si l’horodatage de la transaction est dans le futur ou remonte à plus de constants.maxTransactionAgeSec secondes dans le passé. Il n’est pas utile d’ajouter dans publish les mêmes vérifications que dans verify, car cette dernière doit être appelée après publish. De plus, les vérifications de publish ne peuvent pas être intégrées à verify car elles dépendent de l’heure actuelle, ce qui rend impossible la vérification du fait qu’une ancienne transaction ait moins de 5 secondes. Une application cliente recevra des messages d’erreur tels que The difference between timestamp and timestampMs is greater than ${maxTimestampMsDelta}ms. ou Invalid transaction timestamp. Timestamp is not in the int32 range si ces vérifications ne sont pas dans publish() mais dans verify().
Notes sur les Horodatages
Le champ timestamp de la transaction correspond au temps ADAMANT en secondes depuis l’époque, et non au temps Unix. Le champ timestampMs devrait correspondre au temps ADAMANT en millisecondes depuis l’époque, et non au temps Unix ; les millisecondes Unix peuvent être dérivées via constants.epochTime.getTime() + timestampMs. Le champ timestampMs ne fait pas partie des octets de transaction, des signatures, des ID de transaction ou des hachages, même après l’activation de spaceship. Après spaceship, la validation sensible au consensus devrait exiger que timestampMs soit dans la même seconde ADAMANT que timestamp : 0 <= timestampMs - timestamp * 1000 < 1000. Cette règle plus stricte du même instant est intentionnelle : timestampMs doit affiner exactement la même seconde ADAMANT stockée dans timestamp, et les clients doivent calculer timestamp = Math.floor(timestampMs / 1000). L’utilisation de Math.round() ou Math.ceil() près d’une limite de seconde crée une paire incohérente et doit être rejetée. Une revue des clients et sources actuels a révélé que adamant-api-jsclient, la PWA, adamant-console (via [email protected]), iOS et les exemples de documentation utilisent tous Math.floor ou une troncature équivalente, sans aucun chemin de génération d’horodatage de transaction ADM sortante utilisant round ou ceil. Avant spaceship, la normalisation sensible au consensus devrait supprimer timestampMs afin que le comportement pré-activation reste compatible avec les anciens nœuds. Plusieurs transactions provenant du même expéditeur dans une même seconde sont autorisées tant qu’elles sont des transactions distinctes ; timestampMs aide les clients à trier rapidement les messages de chat, mais ne doit pas devenir un nonce ou une entrée de signature.
Vérifications au Moment de l’Admission
La méthode publish() contient des vérifications d’admission de l’API publique pour les transactions fraîchement soumises, qui peuvent dépendre de l’horloge actuelle du nœud. Si une transaction passe publish(), elle est tout de même traitée par verify(), qui n’a intentionnellement aucune vérification temporelle actuelle et ne doit conserver que des vérifications déterministes telles que le schéma, l’ID, les signatures, les soldes et la règle du même instant pour timestampMs. Les vérifications temporelles de publish() ne doivent pas être déplacées dans verify() car verify() est utilisée pour la relecture, la synchronisation et les transactions historiques. La vérification actuelle du futur est basée sur des créneaux : avec des créneaux de 5 secondes, une horloge cliente en avance d’environ 400 ms ne provoque Transaction timestamp is in the future que lorsque l’horodatage arrondi en secondes franchit le prochain créneau. maxTransactionFutureMs est une tolérance d’admission de l’API publique non sensible au consensus, appliquée uniquement dans publish(), pas dans la relecture, la vérification pair/bloc ou la logique d’activation du consensus. La condition prévue préserve l’ancienne politique d’admission basée sur les créneaux tout en adoucissant le cas limite en rejetant uniquement lorsque transactionSlotNumber > currentSlotNumber et que l’heure de la transaction est en avance de plus de maxTransactionFutureMs par rapport à l’heure actuelle du nœud. Pour ce calcul, utilisez timestampMs lorsque le client l’a fourni ; sinon, revenez à timestamp * 1000. Une petite grâce non sensible au consensus dans publish() (par exemple, 500 ms pour le prochain créneau) peut améliorer l’expérience utilisateur pour les horloges clientes légèrement en avance, sans modifier la validation de relecture, bien que les applications clientes devraient toujours conserver une petite réserve d’horodatage pour la compatibilité avec les anciens nœuds. Une transaction dont le timestamp est en avance d’une seconde par rapport au bloc actuel peut encore être incluse dans ce bloc sans compromettre la blockchain, car le timestamp de la transaction est un métadonnée signée, pas la source de validité du créneau ou du bloc, et le consensus n’exige pas que tx.timestamp <= block.timestamp. Pendant la relecture, les nœuds ne comparent pas les horodatages des transactions avec l’heure locale, donc la grâce au moment de l’admission ne peut pas créer de divergence dans la relecture.
Point de Terminaison d’État
Le point de terminaison /api/node/status peut renvoyer des champs d’horloge supplémentaires tels que nodeTimestampMs (millisecondes depuis l’époque ADAMANT) et unixTimestampMs sans impact sur le consensus. nodeTimestamp reste en secondes depuis l’époque ADAMANT.
Hauteurs d’Activation
Les hauteurs d’activation du consensus doivent être configurables pour le testnet et les chaînes basées sur ADAMANT, tout en préservant les valeurs par défaut du mainnet dans le code ou la configuration. L’ancien contrôle fairSystemActivateBlock: 4359464 utilisait height > fairSystemActivateBlock, donc la hauteur active équivalente pour une activation nommée fairSystem est 4 359 465. L’activation spaceship devrait limiter uniquement les comportements sensibles au consensus, tels que l’acceptation, la normalisation, le stockage et la validation de timestampMs lors du traitement des blocs, des pairs et de la relecture. Les vérifications relatives à l’horloge dans publish() sont une politique d’admission et doivent rester en dehors de l’activation du consensus.