Duniter-squid

Au vu des dernières discussions sur Indexers subsquid, je publie officiellement duniter-squid dans le namespaces “node” du git : https://git.duniter.org/nodes/duniter-squid.

Ça fait un projet de plus dont il faut suivre l’évolution, donc du boulot, mais c’est bon signe qu’on en ait besoin, ça veut dire qu’on rentre dans le concret.

4 Likes

Je viens de lancer mon noeud Squid: https://gdev-squid.axiom-team.fr

J’y ai activé les subscriptions. Toutes les queries (hors queries *Connection réservés à la pagination) peuvent donc être transformé tel quel en subscription, simplement en changeant le mot clé de début de requête query par subscription, exemple:

subscription eventsTransfer {
  events(limit: 10, orderBy: block_height_DESC, where: {pallet_eq: "Balances", name_eq: "Transfer"}) {
    id
    name
    pallet
    args
    block {
      height
    }
  }
}

Pour souscrire aux dernière transactions émises sur le réseau.
Chose fun, lorsque votre squid se synchronise la première fois à votre chaine, vous pouvez suivre en live son état en souscrivant à une table au choix.

Je travail sur la MR !1 pour homogénéiser l’environnement de dev/prod.


Je met ici ma conf nginx pour d’une part rediriger / vers /graphql, et laisser passer les connections websocket:

upstream gdev-squid.axiom-team.fr {
   server       10.10.10.107:10020;
}

[...]

   location / {
      proxy_pass        http://gdev-squid.axiom-team.fr;
      proxy_set_header  X-Real-IP  $remote_addr;
      proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header  X-Forwarded-Proto https;
      proxy_set_header  Host $http_host;
      proxy_redirect    off;

      # for websocket
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
   }

Exemple de requête paginé, pour voir toutes les transactions émises et reçus de mon compte:

query history {
  transfersConnection(where: {from: {id_eq: "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn"}, OR: {to: {id_eq: "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn"}}}, orderBy: blockNumber_DESC_NULLS_LAST, after: null, first: 10) {
    pageInfo {
      endCursor
      hasNextPage
    }
    edges {
      node {
        amount
        id
        timestamp
        from {
          id
          identity {
            name
          }
        }
        to {
          id
          identity {
            name
          }
        }
      }
    }
  }
}

Pagination graphql classique, vous renseignez la valeur de endCursor à after pour passer de page en page. La subtilité c’est qu’ils semblent avoir mixé leur convention de format de curseur avec la pagination par offset, les curseurs étant des nombres entier incrémentales. Plutôt étrange.


Première issue:

1 Like

Je veux bien m’emparer du sujet squid pour la suite, ces changements seront l’occasion de voir comment passer au runtime v702, comment exporter/importer les metadata du runtime ect …

Je ne sais pas si c’est documenté quelque part, sinon ce sera l’occasion de le faire.

Je pense qu’on peut fermer la MR!2 et passer au runtime 702 quand il sera prêt avant de retravailler la gestion des statuts d’identités dans une nouvelle MR, non ?

2 Likes

C’est plus simple de passer entièrement à un nouveau runtime sur une nouvelle chaîne que de gérer un runtime upgrade et d’avoir plusieurs runtimes dans l’historique de la chaîne. En gros, il faudra ajuster tous les switch case :

case events_t.cert.newCert.name:
  let cert: { issuer: IdtyIndex; receiver: IdtyIndex };
  if (events_t.cert.newCert.v700.is(event)) {
    cert = events_t.cert.newCert.v700.decode(event);
  } else {
    throw new Error("Unsupported spec");
  }

D’ailleurs je me demande pourquoi ça marche avec v700 alors qu’on devrait avoir v701, peut-être parce que les métadonnées n’ont pas évolué entre les deux et que je n’ai pas mis à jour.

J’irais pas jusqu’à dire que j’ai documenté comment faire, mais j’ai écrit ça : README-2.md · main · nodes / duniter-squid · GitLab

Et pour la MR !2, c’est comme tu préfères, mais le status pourra être utile de toute façons, donc pourquoi pas l’ajouter.

1 Like

Salut @poka et @HugoTrentesaux

Vraiment, bravo pour l’indexeur subsquid, que Cs² sait maintenant requêter !
C’est très simple à utiliser, et je me rend compte que cela résoud totalement le problème de la génération des notifications côté App : plus besoin de parser les blocs (dans les Cs-Pod) pour que Cesium s’y abonne. L’API de requetage est suffisamment précise pour remplacer tout cela : c’est top !

Bravo également pour le data pod v2.
Je suis enfin (presque) rassurer sur l’architecture de Duniter V2 ! :slight_smile: (“preque” car il faut pouvoir détecter les désynchros des indexeurs et data pod, côté client)

En utilisant ta requete GraphQL pour les TX, je me rends compte qu’elle ne fonctionne pas sur les anciennes TX, qui sont toutes au bloc genesys 0. Mieux vaut utiliser ordonné par timestamp_desc, non ?
Du coup, pour une meilleure tracabilité, que diriez vous de conserver les blocs V1, en le mettant en négatif ? Ainsi les clients pourrait toujours afficher les numéro de bloc V1, et la continuité serait parfaite.
Est-ce faisable ?

Concernant l’historique des certifications, je ne parviens pas à récupérer l’historique : je n’ai aucun résultat…
Voici ma requete, avec l’adresse de @poka.

query CertsConnectionByIssuer($address: String!, $limit: Int!, $orderBy: [CertOrderByInput!]!, $after: String) {
  certsConnection(
    first: $limit,
    after: $after,
    orderBy: $orderBy,
    where: {issuer: {id_eq: $address}}
  ) {
    totalCount
    pageInfo {
      endCursor
      hasNextPage
    }
    edges {
      node {
        id
        createdOn
        creation {
          blockNumber
        }
        issuer {
          id
          name
          membership {
            id
          }
        }
        receiver {
          id
          name
          membership {
            id
          }
        }
      }
    }
  }
}

avec comme variables :

{
  "address": "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn",
  "limit": 10,
  "after": null,
  "orderBy": "id_DESC"
}
2 Likes

Pour ce qui est de la synchro de l’indexeur, on peut requêter le dernier bloc indexé :

query LastBlock {
  blocks(limit: 1, orderBy: height_DESC) {
    height
    hash
  }
}

et comparer le numéro / hash à celui obtenu auprès du noeud.

Amusant, je venais d’ajouter un ticket duniter-squid à ce sujet ! Add timestamp with historical values (#2) · Issues · nodes / duniter-squid · GitLab. Donc oui, c’est faisable et prévu :slight_smile:

Pas besoin de pagination pour les certifications, le résultat sera toujours raisonnablement petit. J’avais mis un exemple de requête pour txels ici : Display certification received and issued (#9) · Issues · clients / Cesium-grp / cesium2s · GitLab

query CertsViewQuery($identity_index: Int!) {
  identities(where: {index_eq: $identity_index}) {
    index
    name
    certIssued(orderBy: createdOn_DESC) {
      receiver {
        name
      }
      createdOn
      active
    }
    certReceived(orderBy: createdOn_DESC) {
      issuer {
        name
      }
      createdOn
      active
    }
  }
}

et j’ai bien un résultat

exemple pour identity 344
{
  "data": {
    "identities": [
      {
        "index": 344,
        "name": "HugoTrentesaux",
        "certIssued": [
          {
            "receiver": {
              "name": "Daigongen"
            },
            "createdOn": 96977,
            "active": true
          },
          {
            "receiver": {
              "name": "bgallois"
            },
            "createdOn": 65140,
            "active": true
          },
          {
            "receiver": {
              "name": "gpsqueeek"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "mathieuBize"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "moul"
            },
            "createdOn": 0,
            "active": false
          },
          {
            "receiver": {
              "name": "vit"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "paidge"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Nadou"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "1000i100"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "dig"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Guenoel"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Felipe"
            },
            "createdOn": 0,
            "active": false
          },
          {
            "receiver": {
              "name": "DYves62"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Fred"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "menfin"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Attilax"
            },
            "createdOn": 0,
            "active": false
          },
          {
            "receiver": {
              "name": "scanlegentil"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "numahell"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "pyg"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "tuxmain"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Fabwice"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "EtienneChouard"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "hocine"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "LeBrice"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "ManUtopiK"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "lanoire"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Maaltir"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Jefferson"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "MatthieuLatapy"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "TomAs"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Maaude09"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "kapis"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "SyoulAnuanua"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Gamaliel"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "GeraldineGarrigues"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "FredTahiti"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "hypericum"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Hdsaf"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Wellno1"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Josetre"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "tatinetteb"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "PhilippeGuillemant"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Gclaire"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "ENO"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "GUL40_Fr1"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Sylharmonie"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Paola"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "EtK"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "davidbp845"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Immae"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Yvv"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "aya"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Mido"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "flodef"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Pini"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "isadel"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Planchou"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "poka"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "Paulart"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "receiver": {
              "name": "nox"
            },
            "createdOn": 0,
            "active": false
          },
          {
            "receiver": {
              "name": "elois"
            },
            "createdOn": 0,
            "active": true
          }
        ],
        "certReceived": [
          {
            "issuer": {
              "name": "cuckooland"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "elois"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "gerard94"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "gpsqueeek"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "moul"
            },
            "createdOn": 0,
            "active": false
          },
          {
            "issuer": {
              "name": "vit"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "PiNguyen"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "CamilleLemasson"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Attilax"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "scanlegentil"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "tuxmain"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "tree"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "hocine"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "LeBrice"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "lanoire"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Maaltir"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "MatthieuLatapy"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "ThomasBourdon"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "kapis"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "SyoulAnuanua"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Gamaliel"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "GeraldineGarrigues"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "FredTahiti"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "JerryBB"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "hypericum"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Wellno1"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "tatinetteb"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Gclaire"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "ENO"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "GUL40_Fr1"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Sylharmonie"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Paola"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "EtK"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "davidbp845"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Ninette89"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "aya"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Mido"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Pini"
            },
            "createdOn": 0,
            "active": true
          },
          {
            "issuer": {
              "name": "Redgg"
            },
            "createdOn": 0,
            "active": false
          }
        ]
      }
    ]
  }
}
2 Likes

Ben si, car cela dépend de l’usage. Rien n’empêche d’exploiter les données pour une graphique, etc. via un requeteur quelconque.
De plus, il peut y avoir jusqu’à 100 certificiations, plus l’historique (par exemple celles non renew).
Et dans une liste affichée sur un téléphone mobile, seule 10-15 certifications sont visibles dans l’écran. Rien ne sert d’en recupérer plus…

La question : faut il coder quelque chose pour que les requête xxxConnection fonctionne, dans subsquid ?

Ok, dans ce cas, il faut faire la bonne requête, parce que là, l’adresse ne peut pas être utilisée comme identifiant de l’émetteur d’une certification.

query MyQuery {
  certsConnection(orderBy: id_DESC, where: {issuer: {account: {id_eq: "5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn"}}}) {
    totalCount
  }
}
{
  "data": {
    "certsConnection": {
      "totalCount": 19
    }
  }
}

Pour plus de détails, tu peux lire le schéma et ses commentaires, mais en gros l’idée c’est :

  • toute donnée a comme “id” l’event id responsable de sa création
    • genesis-xxx pour ce qui est créé au genesis
    • blocknumber-blockhash-eventnumber pour le reste
    • sauf pour les comptes qui ont comme id l’adresse ss58
  • une certification a comme issuer et receiver des identittés
  • les identités ont forcément un compte (qui peut changer)
  • les comptes peuvent avoir une identité associée, mais pas nécessairement
ta requête complète corrigée
query CertsConnectionByIssuer($address: String!, $limit: Int!, $orderBy: [CertOrderByInput!]!, $after: String) {
  certsConnection(
    first: $limit,
    after: $after,
    orderBy: $orderBy,
    where: {issuer: {account: {id_eq: $address}}}
  ) {
    totalCount
    pageInfo {
      endCursor
      hasNextPage
    }
    edges {
      node {
        id
        createdOn
        creation {
          blockNumber
        }
        issuer {
          id
          name
          membership {
            id
          }
        }
        receiver {
          id
          name
          membership {
            id
          }
        }
      }
    }
  }
}
la réponse
{
  "data": {
    "certsConnection": {
      "totalCount": 19,
      "pageInfo": {
        "endCursor": "10",
        "hasNextPage": true
      },
      "edges": [
        {
          "node": {
            "id": "genesis-61-970",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-970",
              "name": "MamieCrypto",
              "membership": {
                "id": "genesis-970"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-8352",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-8352",
              "name": "Yvv",
              "membership": {
                "id": "genesis-8352"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-6806",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-6806",
              "name": "Paola",
              "membership": {
                "id": "genesis-6806"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-6716",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-6716",
              "name": "GUL40_L21",
              "membership": {
                "id": "genesis-6716"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-6656",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-6656",
              "name": "GUL40_K1r",
              "membership": {
                "id": "genesis-6656"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-6622",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-6622",
              "name": "GUL40_Fr1",
              "membership": {
                "id": "genesis-6622"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-6322",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-6322",
              "name": "kolsim",
              "membership": null
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-24",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-24",
              "name": "Paulart",
              "membership": {
                "id": "genesis-24"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-2058",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-2058",
              "name": "LeBrice",
              "membership": {
                "id": "genesis-2058"
              }
            }
          }
        },
        {
          "node": {
            "id": "genesis-61-1796",
            "createdOn": 0,
            "creation": [],
            "issuer": {
              "id": "genesis-61",
              "name": "poka",
              "membership": {
                "id": "genesis-61"
              }
            },
            "receiver": {
              "id": "genesis-1796",
              "name": "Damery",
              "membership": {
                "id": "genesis-1796"
              }
            }
          }
        }
      ]
    }
  }
}
1 Like

Par contre, pour l’historique de certification, la logique est différente. Une certification A → B est unique, mais elle peut être associée à un historique de créations, renouvellements, suppressions. Exemple :

query MyQuery {
  identities(limit: 1) {
    name
    certIssued(limit: 1) {
      receiver {
        name
      }
      active
      creation {
        id
      }
      removal {
        id
      }
      renewal {
        id
      }
    }
  }
}
{
  "data": {
    "identities": [
      {
        "name": "Maaltir",
        "certIssued": [
          {
            "receiver": {
              "name": "vincentux"
            },
            "active": false,
            "creation": [],
            "removal": [
              {
                "id": "0000494320-74c09-000000"
              }
            ],
            "renewal": []
          }
        ]
      }
    ]
  }
}

la certification Maaltirvincentux est un objet unique, mais elle peut être associée à une liste de créations, renouvellements, retraits. Ici la création n’est pas disponible, parce qu’elle a eu lieu avant le genesis (mais on pourrait l’ajouter), et elle a expiré au bloc 494320. Si @Maaltir re-certifie @vincentux, il y aura une nouvelle création, et si elle expire à nouveau, un nouveau retrait.

ok, je récupère bien les certifications, maintenant.

Ce qui me manque, c’est juste la date du dernier évenement (create/renew/remove) Je ne sais pas trop comment faire : faut il faire un max(max(<creation>), max(<renewal>)) ? Puis aller cherche les info du bloc ?

D’aiilleurs, je m’apercois que mon comptage du “nombre de certitications recues” n’est pas bon, car il faut plutot compter le nombre de certification recues ET active. Sinon je vais compter les anciennes (qui sont removal), non ?

1 Like

Su gecko je récupère tout ce que je peux depuis le storage Duniter directement. C’est le cas du nombre de certification émis/reçus:

Sinon oui il faut filtrer les certifications actives sur squid.

1 Like

Bonne idée, on pourrait ajouter un champ “lastTouched” sur l’identité et la certification pour faciliter ça. Pour l’instant l’indexeur est assez minimal, il ne faut pas hésiter à ajouter des champs si ça simplifie les traitements client. Ça te convient comme proposition ?

Si tu veux afficher le nombre de certifications reçues actives, oui, il faut filtrer par active. Comme dit @poka, ce genre d’infos qui sont indexées par Duniter peuvent être récupérées directement sur l’API RPC. Mais on pourrait également dupliquer ce champ côté indexeur, ce n’est pas gourmand ni trop compliqué. On peut ajouter certIssuedActiveCount et certReceivedActiveCount à l’index si c’est pratique.

La “date d’une certification” c’est la date de son dernier renouvellement ou de sa création, c’est bien ça ? Dans ce cas, ça peut rentrer dans le champ “lastTouched”. Pour le timestamp, on a deux stratégies :

  • soit on l’ajoute partout comme proposé dans #2
  • soit on ajoute une foreign key sur le bloc à la place ou en complément des “blockNumber” pour récupérer directement les infos dans le bloc en une seule requête

Les deux stratégies peuvent être combinées, bien sûr. Maintenant que tu me le dis, je pense que la foreign key serait plus puissante et pas forcément encombrante, je vais créer un ticket pour ça (edit : #4).

1 Like

Non non l’idée c’est d’ajouter tout ce qu’on à besoin à l’indexer sans avoir à reparser les blocs derrière, on va ajouter ce qu’il manque.

Oui on va déjà ajouter les dates en created_at en plus des numéros de blocs en created_on de la même façon que @ManUtopiK avait fait avec duniter-indexer.
Oui on peut ajouter ce champs lastTouched, à voir ce qu’on y met en plus de la date, probablement l’id de l’event pour pouvoir pointer dessus directement.
Je trouve le schema actuel un peu lourd, j’ai une sensation de doublonnage entre le status d’identité et les champs memberships, je vais peut être d’abords voir pour simplifier ça si possible, ou pas.
Je verrais ça dès que j’ai un peu de temps, peut être demain.

3 Likes

Mais du coup ça oblige à avoir une sacrée confiance dans l’indexer ? Ou je loupe un truc ?

1 Like

Finalement je suis plutôt d’avis de mettre une foreign key sur le bloc ce qui permet de récupérer le timestamp dans une seule requête sans dupliquer l’info dans autant de tables qu’il y a d’événements dans le bloc.

Pareil ici, une foreign key sur l’événement. L’id c’est juste une contrainte technique de squid, c’est pour ça que j’ai mis l’event-id, mais quand c’est un champ, il vaut mieux mettre une foreign key. En plus, à partir de l’event, on peut remonter au call, à l’extrinsic, au bloc :

image

Oui, on a la même sensation côté Duniter, et @cgeek avait proposé de fusionner tout ça, mais je n’étais pas encore prêt mentalement à franchir le pas (cf Résistance au changement, la nuit porte conseil). Mon idée pour l’instant est que le status est une forme d’index pour éviter d’avoir à filtrer sur un champ lié.

Oui, tout à fait, si on se fie uniquement à l’indexeur, on peut être manipulé par lui. Deux types de mensonges sont possibles : ajout/modification d’information et omission.
Pour l’ajout/modification, c’est pas trop compliqué, il suffit d’aller vérifier dans le bloc ciblé, et si ça ne correspond pas, c’est la blockchain qui a raison (donc c’est soit un bug soit une “attaque” de l’indexeur).
Pour le mensonge par omission, c’est plus compliqué. Si un indexeur ne donne pas une info, la seule manière de s’en apercevoir est de trouver l’info non indexée en blockchain. Cf ce message : Quel indexeur utiliser et scan réseau - #2 by HugoTrentesaux

1 Like

@Maaltir oui c’est sûr que les instances d’indexers doivent être digne de confiance.
C’est une excellente remarque, et subsquid semble fournir des réponses techniques aidant à ancrer cette confiance:

  • Trust-minimized queries: the data can be audited, and all clients can verify the query result

[…]

  • Node operators have to bond a security deposit, which can be slashed for byzantine behavior

Je n’ai pas vue ce genre de dépôt de garanti sur nos instances, est-ce une configuration particulière, ou parlent-ils de leur réseau de cloud ?

  • Any query can be verified by submitting a signed response to an on-chain smart contract

Et surtout ce paragraphe sur la validation des requêtes: https://docs.subsquid.io/subsquid-network/whitepaper/#query-validation

Je n’ai pas encore tout lu/compris à ce sujet, ça mérite d’être creuser je pense @HugoTrentesaux

Oui mais justement on ne va pas demander aux client d’aller vérifier chaque contenu de bloc pour chaque donnée, enfin je ne sais pas, ça me parait sous efficient.

Ah oui ok je te fait entièrement confiance sur ce genre de chose, on en discutera en MR j’aime bien notre manière de faire en ping pong ^^
Il faut que je prenne le temps d’étudier ce schéma, les différentes possibilités et de tester.

2 Likes

Peut-être, mais ça me semble compliqué. Alors que notre modèle de confiance est simple : on fait confiance aux gens de la toile. S’ils trahissent notre confiance, ça ne leur coûte pas des Ǧ1, mais ils y laissent leur honneur. C’est à mon avis autrement plus précieux. Donc mon approche serait plutôt : on fournit une liste signée d’indexeurs de confiance. Par exemple l’indexeur Axiom serait signé par les tech d’Axiom, et on se fait nos audits entre nous pour vérifier qu’on est bien sécurisé. Pareil pour les réseaux espagnols, ils mettent une infra en place et les tech qui la contrôlent signent un document qui atteste de leur confiance.
Et après on peut toujours penser que “la confiance n’exclut pas le contrôle” et réaliser des contrôles de routine, avec des rapports de sécurité, et éditer des blacklist si des membres mettent à dispo des indexeurs menteurs… mais pour moi c’est chercher à résoudre un problème qu’on n’a pas. Et je préfère commencer par résoudre les problèmes qu’on a.

3 Likes

Entièrement d’accord. Pour moi le rôle de l’indexeur est de fournir tous les besoins des clients. Il se doit d’être le plus complet possible dans les infos qu’il fournit. L’idéal serait que le client ne fasse appel que très rarement à l’API RPC.

Si Squid a des particularités techniques qui rendent des cas d’usage difficile à implémenter, alors le parsing d’un bloc doit être une solution temporaire en attendant de réaliser l’implémentation complète du cas d’usage. Amha.

2 Likes

Non Squid n’a aucune restriction particulière concernant son implémentation, on index vraiment ce qu’on veut à partir des événements :slight_smile:

Après à nous d’optimiser le schéma en l’organisant correctement.

La question était plus sur la confiance aveugle qu’on lui accorde, doublé de vérification onchain ou d’autre mécanismes

1 Like

Cependant, dans l’exemple concret ci-dessus, où il était proposé d’ajouter une foreign key sur le bloc, n’y a t’il pas des contraintes lié au rollback d’un bloc ?
Comment sont géré les rollbacks et les bloc non finalisées, dans subsquid ?
Es-ce par les delete cascade du bloc, où bien par ceux des évenements ?