Paiement simplifié avec GVA: génération du document transaction coté serveur

Voila qui est fait : [feat] gva:gen_tx: exclude inputs in mempool on inputs search

Ce fût plus difficile que prévu, notamment car je voulais gérer correctement tous les cas (script quelconque), même si GVA n’expose pas encore cette fonctionnalité.

@poka tu peux tester 2 tx de suite avec le même issuer pour voir si ça fonctionne ? J’ai déjà testé via un TU + sur mon nœud local mais plus il y a de tests mieux c’est.

Pour mettre à jour ton nœud tu peux utiliser la commande suivante :

git fetch && duniter stop && git reset --hard origin/gva-proto-2 && cargo xtask build --production && duniter webstart --gva

EDIT: pas besoin de resync évidemment. Si je ne précise pas c’est qu’il n’y a pas besoin :wink:

1 Like

Mon noeud local s’est mis à jour/rebuild sans soucis avec ta commande.
J’ai effectué 3 transactions à la chaine:

Je les vois sur Cesium, j’éditerais quand elles seront validés :slight_smile:

(Inscrites au bloc 373703)

2 Likes

Eh ben voila, comme quoi ce n’est pas si compliqué de compiler Duniter :grin:

Génial ça fonctionne nickel :smiley:

3 Likes

Alors j’ai travaillé là-dessus hier soir et ce soir, et je me suis rendu compte qu’il est impossible de générer toutes les transactions intermédiaires cotés serveur en l’état actuel du protocole, voici pourquoi :

Les sources de monnaies provenant de transactions antérieures sont identifiées par le hash de la transaction d’origine, or ce hash se calcule à partir du document complet avec signature: il est donc impossible de générer en même temps plusieurs transactions chaînées sans posséder la clé privée de l’émetteur.

On peut évidemment réfléchir à la redéfinition du hash d’une transaction pour une future version du protocole mais ça attendra, car je ne souhaite pas intégrer de nouvelles modifications à DUPBv13 autre que celles prévues afin d’éviter un effet tunnel trop important.

parenthèse effet tunnel

Déjà qu’il n’y a pas eu de nouvelle version de Duniter depuis 5 mois alors que je voulais releaser plus souvent :

Fin septembre j’étais à deux doigts de commencer la campagne de test de duniter 1.9, c’est suite aux demandes de @kimamila que j’ai décidé de retarder la release pour y intégrer un prototype de GVA + réintégrér certains changements du DUBPv13 que j’avais prévu de reporter.


Bref tout ça pour dire que, en attendant, je souhaite tout de même permettre le paiement simplifié de gros montant sans changer le protocole. Voici comment :

Avec deux échanges supplémentaires entre le client et le serveur pour chaque niveau de profondeur.
J’ai conçu un algorithme qui minimise le niveau de profondeur, ce dernier ne sera que de 2 dans la très grande majorité des cas.
Voici un tableau qui résume le nombre d’aller-retour client<->serveur en fonction du nombre de sources de monnaie nécessaire :

Nombre de sources Profondeur AR client-serveur Nombre de tx doc
1-40 1 2 1
41-1600 2 4 2-41
1601-64000 3 6 42-1641
64001-2560000 4 8 1642-65641

On peut continuer le tableau à l’infini, mais dans 99.99% des cas on sera dans la 2ème ligne, même pour des très gros montant, c’est à dire 4 AR client-serveur au lieu de 2 :slight_smile:

L’implémentation est presque terminée, je gère déjà la génération des transactions de change, ce qu’il me manque c’est la prise en compte des sources de monnaie en mempool parmi les sources de monnaies utilisables, détail absolument nécessaire pour que les cas de profondeur 2 ou plus puisse fonctionner sans avoir besoin d’attendre qu’un bloc (voir plusieurs) passe entre chaque étape.

Il est donc théoriquement déjà possible d’envoyer de gros montant via le paiement simplifié de GVA à condition d’avoir beaucoup de temps pour le faire, mais ce n’est pas très pratique, il vaut mieux attendre que j’implémente le «détail» ci-dessus.

2 Likes

Si je comprends, cela signifie qu’actuellement les sources exploitables via GVA sont uniquement celles provenant de son propre DU ?
Les sources provenant de d’autres transactions étant inexploitables sans la clé privé de l’émetteur pour retrouvé leur hash ?

Pas sûr d’avoir bien compris mais bon je ne connais pas bien comment Duniter gère ses sources.
En attendant j’ai pas mal amélioré mon micro client GVA pour en faire quelque chose de plus générique.
Il me semble tout à fait exploitable en l’état.

Il ne manquera donc que la gestion des différents aller/retour en fonction du niveau de profondeur à priori.

1 Like

Pourquoi 40 ?

pour une tx avec 1 issuer :

  • avec 2 outputs (recipient et backchange) on a au max 46 sources

  • avec 1 output (tx de change) on a au max 47 sources.

edit : @poka j’ai détaillé le calcul ici.

Où vois-tu ça ?
Je ne trouve pas dans la RFC.

Non toutes les sources sont déjà exploitables. Mais s’il y en a plus de 40 il y a plusieurs aller-retour.

Pour rester KISS. Je sais pertinemment qu’il y a plus optimal, mais il est préférable d’avoir un algo quasi-optimal qui traite correctement tous les cas avec une complexité relativement faible qu’un algo totalement optimal qui explose la complexité de gestion de certains cas.

Mon but est de produire une codebase qui soit maintenable par mes successeurs, donc toute complexité non nécessaire est a évitée.

Toutefois pour le cas des paiements simples uniquement (1 seule pubkey qui envoie un montant à une seule autre pubkey) je peux modifier la constante à 46 :slight_smile:

4 Likes

Alors celle-là on me l’avait jamais faîte ! Si on modifie une constante c’est pas une variable ? :rofl:

Ok, je sors… :running_man:

2 Likes

Voila qui est implémenté, le commit : [feat] gva:gentxs: use pending outputs on mempool for inputs search (97659203) · Commits · nodes / typescript / duniter · GitLab

Il est donc désormais possible d’envoyer un gros montant via le paiement simplifié de GVA, voici comment faire :

  1. Demander la génération du/des documents transaction·s comme avant, si les 46 premières sources ne suffissent pas, des transactions de change seront générées:


  1. Signer puis soumettre la ou les transaction·s de change :

  1. Refaire la requête d’origine avec le paramètre useMempoolSources à true:

  1. Soit vous avez enfin la transaction finale (c’est le cas dans mon exemple), soit il faut recommencer les étapes 2 et 3 autant de fois que nécessaire :slight_smile:

Voilà il est désormais possible d’envoyer tout montant par paiement simplifié GVA (sous réserve d’avoir un solde suffisant).

2 Likes

Joli travail !

Est-ce que ce ne serais un bug ici, le document généré contient le commentaire « change » mais ne semble pas contenir de transaction de change ?
L’output est de 46736 au lieu de 47000, sans change. (dernier persion proto GVA, sur mon noeud comme sur le tiens) :

Généré avec:

{
  genTxs (
    amount: 47000
    issuer: "Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P"
    recipient: "AhEDcWnSW4SdzidsDYhnDfs75DBpKQKDgHzJzrXdMmB9"
    comment: "Test de change !!"
  )
}

Je constate qu’en passant le amount de 47000 à 47800, le document reçus contient bien la transaction de change:

1 Like

Et pourtant sur ta screen on voit bien la transaction de change, tout fonctionne comme attendu, je ne comprends pas ce qui te chiffonne ?

Non ce que tu vois c’est juste un changement du nombre de transactions de change (2 au lieu de 1) :slight_smile:

Ok c’est que je n’ai pas dû bien comprendre comment sont gérés ces transactions de change.

J’ai compris la nécessité des 2 transactions dans le seconds cas car je passe de 46 à 48 sources, donc du premier palier au seconds.

Mais je n’ai pas compris comment identifier les transactions de changes alors, la seul différence que je vois c’est que le output n’est pas à la hauteur du amount lorsqu’il y a du change.

Entre un amount à 47000 et 47500 les documents générés sont strictement identiques (hormis le blockstamp bien sûr).

C’est qu’il va fusionner les 46 sources en une seul c’est ça, donc au deuxième essai on est sûr de n’avoir besoin que de cette nouvelle grosse source et quelques autres ?

(Je cherche dans la RFC je ne trouve pas …)

Dans le premier cas tu as besoin de 47 sources pour envoyer le montant désiré. Comme ce n’est pas possible directement (le max étant 46). Il est donc nécessaire de compacter une partie des sources pour réduire le nombre de sources afin que l’envoi devienne possible :slight_smile:

En effet c’est une manière de les différencier. Mais le plus simple c’est de regarder le destinataire du output, ce qui caractérise une transaction de change c’est le fait que le receveur soi le destinataire :wink:

Exactement :smiley:

Normal ce n’est pas protocolaire. Le protocole défini les règle à respecter, ce n’est pas son rôle de dire comment coder une implémentation en fonction de ce que l’on veut faire (ici envoyer un gros montant avec beaucoup de petites sources).

2 Likes

Ok merci c’est limpide :slight_smile:

En plus je le savais, j’ai dû traiter ce cas lorsque j’ai codé g1stats …

En plus j’avais un moyen simple de m’en rendre compte avec mon proto-client actuel lol

1 Like

Le document peut également être corrompu à cause d’un bug. Inviter directement à ne plus faire confiance me semble excessif et flou.
Tu pourrais par exemple écrire:

«La transaction générée par le serveur “g1.domaine.tld” semble incorrecte. Le serveur “g1.domaine.tld” est peut-être bugué ou malveillant, merci de signaler ce problème sur le forum Duniter en copiant le rapport d’erreur ci-après. En attendant une réponse, utilisez un autre nœud Duniter. »

Puis tu génères un rapport d’erreur qui peut être la diff entre l’attendu et le constaté.

Ce qui aurait l’avantage de nous permettre d’être alerté rapidement en cas de présence d’un nœud malveillant sur le réseau et d’impliquer les utilisateurs dans la détection des bugs :slight_smile:

3 Likes

Je crois savoir la raison du comportement qui va suivre mais dans le doute je l’explique quand même.
J’ai commencé par envoyer 100Ḡ1 sur une nouvelle clé: Dz64d7msKK2LvSJE9DM4c8oAgRvrKjh1wPY88fnmbQav

Ensuite une fois la transaction validé en blockchain, j’ai voulu envoyer des suites de transactions de 1Ḡ1 depuis cette clé vers une autre clé.

J’ai pu en envoyé une, puis à la seconde GVA me répondait insufficient balance.
5 minutes plus tard j’ai réessayé toujours 1Ḡ1, ça marche, puis de nouveau une 1Ḡ1 de nouveau en échec pour la même raison.

La seconde bonne transaction était visible en attente immédiatement sur Cesium, puis à finit en échec quelques minutes plus tard.

Je suppose que c’est parceque la seule source étant de 100Ḡ1, lors de l’envoi de 1ḡ1 le noeud a effectué une transaction de change de 99ḡ1 ? Du coup tant que la transaction de 1Ḡ1 n’est pas validé je ne peux pas consommer le reste de la source ?

Une transaction valide détruit des sources (UD ou UTXO) et génère des UTXO.

UTXO : Unspent TXransaction Output.

La somme des sources entrantes doit être égale à la somme des UTXO sortantes. Dans la situation que tu décris, il y a 1 source de 10000 qui va être sépaéré : 1UTXO de 100 vers le destinataire et 1 de 9900 vers l’émetteur. Le 2e UTXO est le backchange : la tx “rend la monnaie”.

C’est ce backchange qui va être utilisé comme source pour la tx suivante. Mais tant qu’il n’est pas en BC, il est dans la pool. (edit- c’est la tx qui est en pool, pas le backchange seul). Donc je suppose que tu dois passer le paramètre useMempoolSources à true pour pouvoir envoyer une 2e tx de suite.

(Je ne sais pas si useMempoolSources autorise l’utilisation de sources en piscine, ou s’il oblige à utiliser uniquement des sources en piscine)

4 Likes

Oui @matograine à bien répondu, le comportement que tu observes est tout à fait normal @poka :slight_smile:

useMempoolSources autorise l’utilisation de sources en piscine, comportement désactivé par défaut pour 2 raisons :

  1. Pour des raisons de performances.
  2. Pour éviter de faire grimper la profondeur de chaînage des transactions au-delà du nécessaire, ce qui aurait pour conséquence de retarder certains paiements pour rien.
2 Likes

Parfois sur certaines transactions passant par une étape de change, le document final renvoyé par GVA ne semble pas générer un backchange correct:

$ ./pay.py -d AhEDcWnSW4SdzidsDYhnDfs75DBpKQKDgHzJzrXdMmB9 -c "GVA: $(date '+%d-%m-%y - %H:%M:%S')" -m 70000 -v
Le document contient une transaction de change
Le document généré est conforme.
Echec de la transaction:
Not same sum of inputs amount and outputs amount: (SourceAmount([0, 0, 0, 0, 0, 1, 136, 238, 0, 0, 0, 0, 0, 0, 0, 0]), SourceAmount([0, 0, 0, 0, 0, 2, 191, 211, 0, 0, 0, 0, 0, 0, 0, 0])).
Version: 10
Type: Transaction
Currency: g1
Blockstamp: 374815-0000002B54A002AEC8229C672CB8F7DFF4FA727C0725251010CC10DC8B8AE0B6
Locktime: 0
Issuers:
Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P
Inputs:
43950:0:T:8B37338D7807E58AA55FA1D5121E95C7E1CC52555486C2E8C1C008E15D552F4C:1
56640:0:T:BBA1724A2191B53FDB0474662DB69B3328C1937510968F7A0B2105C19D8821A9:0
Unlocks:
0:SIG(0)
1:SIG(0)
Outputs:
70000:0:SIG(AhEDcWnSW4SdzidsDYhnDfs75DBpKQKDgHzJzrXdMmB9)
110179:0:SIG(Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P)
Comment: GVA: 19-11-20 - 18:24:30
Pc26+B9/PJosgSlSYL2S58aMLkoUg9ntbOGq6l6cS+VO0bPkex8emunhH/s23GZ5IGYDMPzn1cFmrZ4gmqIsBg==

En effet ici le backchange devrait être de 30590 hors il est de 110179, soit plus que le total des inputs qui est de 100590.

Rejouer la même transaction une seconde fois manuellement fonctionne.

Est-ce un bug ou encore une fois une mauvaise compréhension/implémentation de ma part ?

:thinking:

1 Like