Passa al contenuto principale

Avvio rapido

Questa guida mostra come derivare le chiavi di un account, firmare una transazione sulla corsia Native con entrambe le chiavi e verificarla, offline, quindi, facoltativamente, inviarla alla testnet qorechain-diana. Richiede Node.js 20 o successivo. Riguarda QoreSign 0.1.1. Le sezioni dopo il passo 4 mostrano la stessa libreria in Python e Go, e come rileggere una transazione confermata.

1. Installazione​

mkdir qoresign-quickstart && cd qoresign-quickstart
npm init -y
npm pkg set type=module
npm install @qorechain/qoresign

2. Derivare le chiavi​

// keys.mjs
import { addressFromPublicKey, deriveCanonicalPqKey, deriveClassicalKey } from "@qorechain/qoresign";

// The public BIP-39 test phrase. Never use it for real funds.
const mnemonic =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";

const classical = deriveClassicalKey({ mnemonic }); // m/44'/118'/0'/0/0
const { bech32 } = addressFromPublicKey(classical.publicKey, "secp256k1");
const pq = deriveCanonicalPqKey({ mnemonic, address: bech32 });

console.log(bech32, pq.publicKey.length); // qor1..., 2592

deriveCanonicalPqKey restituisce la stessa chiave ML-DSA-87 che i wallet QoreChain derivano per l'account, quindi coincide con la chiave già registrata per gli account da essi creati. Vedi Derivazione delle chiavi.

3. Firmare e verificare​

Un wallet riceve normalmente dalla dApp bodyBytes, authInfoBytes, il chain id e il numero di account (il SignDoc di una richiesta signDirect del Cosmos SDK). Per questo esempio, costruiscili con cosmjs-types:

npm install cosmjs-types @cosmjs/proto-signing @cosmjs/amino
// sign.mjs
import { encodeSecp256k1Pubkey } from "@cosmjs/amino";
import { encodePubkey, makeAuthInfoBytes } from "@cosmjs/proto-signing";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
import { TxBody } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import {
addressFromPublicKey,
deriveCanonicalPqKey,
deriveClassicalKey,
signHybrid,
verifyHybridTx,
} from "@qorechain/qoresign";

const mnemonic =
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const classical = deriveClassicalKey({ mnemonic });
const address = addressFromPublicKey(classical.publicKey, "secp256k1").bech32;
const pq = deriveCanonicalPqKey({ mnemonic, address });

const bodyBytes = TxBody.encode(
TxBody.fromPartial({
messages: [
{
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: MsgSend.encode({ fromAddress: address, toAddress: address, amount: [{ denom: "uqor", amount: "1" }] }).finish(),
},
],
}),
).finish();
const authInfoBytes = makeAuthInfoBytes(
[{ pubkey: encodePubkey(encodeSecp256k1Pubkey(classical.publicKey)), sequence: 0 }],
[{ denom: "uqor", amount: "20000" }],
200000,
undefined,
undefined,
1, // SIGN_MODE_DIRECT
);

const chainId = "qorechain-diana";
const signed = signHybrid({ chainId, accountNumber: 0, bodyBytes, authInfoBytes, classicalKey: classical, pqKey: pq });

const result = verifyHybridTx({
txBytes: signed.txBytes,
chainId,
accountNumber: 0,
classicalPublicKey: classical.publicKey,
pqPublicKey: pq.publicKey,
});
console.log(result); // { valid: true, classical: true, pq: { valid: true, signBytesVersion: 'v2' } }
node sign.mjs

4. Inviare sulla testnet (facoltativo)​

Usa un tuo account testnet (mai una mnemonica mainnet negli script). L'account ha bisogno di QOR testnet e di una chiave ML-DSA-87 registrata; i wallet QoreChain la registrano alla creazione dell'account, e examples/register-and-send mostra come registrarla con QoreSign.

import { simulateTx, submitWithSignBytesFallback } from "@qorechain/qoresign/network";

const rest = "https://api-testnet.qore.host";
const sim = await simulateTx({ rest, txBytes: signed.txBytes }); // throws if the network would refuse it
console.log(sim.gasUsed, sim.suggestedGasLimit, sim.balanceDeltas);
// Rebuild authInfoBytes with gas limit sim.suggestedGasLimit and fee >= 0.1 uqor per gas, sign again, then:
// await submitWithSignBytesFallback({ rest, chainId, sign: () => signedAgain.txBytes });

Il flusso completo, comprensivo di numero di account, sequence e commissione, si trova in examples/register-and-send.

5. Altri linguaggi: firmare e verificare un messaggio​

Questi esempi sono offline. Ognuno deriva le chiavi dalla frase di test pubblica BIP-39 (indirizzo qor19rl4cm2hmr8afy4kldpxz3fka4jguq0agt672h), firma un messaggio con entrambe le chiavi e lo verifica, poi stampa qor19rl4cm2hmr8afy4kldpxz3fka4jguq0agt672h verified. Non finanziare mai quella frase.

Python​

Novità della 0.1.1. Python 3.9 o successivo; wheel per macOS e Linux (nessuna wheel per Windows per ora).

pip install qorechain-qoresign
import qoresign as qs

# Public BIP-39 test phrase: never send funds to it.
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
classical = qs.derive_classical_key(mnemonic, qs.ClassicalKeyType.SECP256K1) # m/44'/118'/0'/0/0
account = qs.address_from_public_key(classical.public_key, qs.ClassicalKeyType.SECP256K1)
pq_key = qs.derive_canonical_pq_key(mnemonic, account.bech32)

data = b"Hello from QoreSign"
sig = qs.sign_message("qorechain-diana", account.bech32, data, classical, pq_key=pq_key)
# Raises QoreSignError if either signature is invalid.
qs.verify_message("qorechain-diana", account.bech32, data, sig, pq_public_key=pq_key.public_key)
print(account.bech32, "verified")

L'esempio è stato eseguito con le wheel 0.1.1 pubblicate su macOS arm64 e Linux aarch64.

Go​

go get github.com/qorechain/qoresign/go@v0.1.1
package main

import (
"fmt"

qoresign "github.com/qorechain/qoresign/go"
)

func main() {
// Public BIP-39 test phrase: never send funds to it.
mnemonic := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
classical, _ := qoresign.DeriveClassicalKey(mnemonic, qoresign.DeriveClassicalOptions{}) // m/44'/118'/0'/0/0
account, _ := qoresign.AddressFromPublicKey(classical.PublicKey, qoresign.Secp256k1)
pq, _ := qoresign.DeriveCanonicalPqKey(mnemonic, account.Bech32)

data := []byte("Hello from QoreSign")
sig, _ := qoresign.SignMessage(qoresign.SignMessageArgs{ChainID: "qorechain-diana",
Signer: account.Bech32, Data: data, ClassicalKey: classical, PqKey: &pq})
// Returns an error if either signature is invalid.
err := qoresign.VerifyMessage(qoresign.VerifyMessageOptions{ChainID: "qorechain-diana",
Signer: account.Bech32, Data: data, Signature: sig, PqPublicKey: pq.PublicKey})
fmt.Println(account.Bech32, "verified:", err == nil)
}

L'esempio Go ignora gli errori per restare breve; controllali nel codice reale. Rust, Swift e Kotlin seguono lo stesso flusso con gli stessi nomi di funzione nella propria convenzione di maiuscole/minuscole (vedi la panoramica). Gli artefatti Kotlin e Android richiedono il compilatore Kotlin 2.3 o successivo: un progetto con Kotlin 2.1 non compila rispetto a essi.

6. Leggere una transazione confermata (ispettore delle transazioni)​

Novità della 0.1.1, TypeScript. Legge soltanto e non invia nulla. Riporta come è stata protetta una transazione confermata: i suoi messaggi, il firmatario, la commissione, se la firma classica è valida, lo stato del livello ML-DSA-87 e il livello di sicurezza (pqc, enrollment, shake o classical). Per i campi vedi Ispettore delle transazioni.

// inspect.mjs
import { fetchEvmWindow, fetchPqKeyStatus, inspectTransaction } from "@qorechain/qoresign/network";

const rest = "https://api-testnet.qore.host";
const address = "qor178nscfyp0cq2ac5x7un4u98xrj4ayp6pt0kw08"; // public testnet faucet account

const status = await fetchPqKeyStatus({ rest, address });
console.log("ML-DSA-87 key registered:", status.registered);

const tx = await inspectTransaction({ rest, chainId: "qorechain-diana", txhash: process.argv[2] });
console.log(tx?.tier, tx?.pqLayer, tx?.messages.map((m) => m.typeUrl));

const win = await fetchEvmWindow({ rest, address });
console.log("EVM window:", win.found, win.live);
node inspect.mjs <txhash>

Usa l'hash di una recente transazione della testnet, per esempio un trasferimento del faucet. Eseguito così, ha stampato ML-DSA-87 key registered: true, tier pqc, pqLayer { present: true, count: 1, algorithmId: 1, verified: true, signBytesVersion: 'v2' }, il messaggio /cosmos.bank.v1beta1.MsgSend e EVM window: false false. inspectTransaction restituisce undefined per un hash che la rete non conosce. Le vecchie transazioni della testnet potrebbero non esserci più perché i nodi della testnet eliminano la cronologia.

Passi successivi​