Sari la conținutul principal

Pornire rapidă

Acest ghid parcurge derivarea cheilor unui cont, semnarea unei tranzacții de pe lane-ul nativ cu ambele chei și verificarea ei, offline, apoi, opțional, trimiterea ei pe testnetul qorechain-diana. Necesită Node.js 20 sau mai nou. Acoperă QoreSign 0.1.1. Secțiunile de după pasul 4 arată aceeași bibliotecă în Python și Go, precum și cum se citește înapoi o tranzacție confirmată.

1. Instalare​

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

2. Derivarea cheilor​

// 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 produce aceeași cheie ML-DSA-87 pe care o derivează portofelele QoreChain pentru cont, deci se potrivește cu cheia deja înregistrată pentru conturile create de ele. Vezi Derivarea cheilor.

3. Semnare și verificare​

Un portofel primește de obicei bodyBytes, authInfoBytes, id-ul lanțului și numărul contului de la dApp (SignDoc al unei cereri signDirect din Cosmos SDK). Pentru acest exemplu, construiți-le cu 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. Trimitere pe testnet (opțional)​

Folosiți propriul cont de testnet (niciodată o mnemonică de mainnet în scripturi). Contul are nevoie de QOR de testnet și de o cheie ML-DSA-87 înregistrată; portofelele QoreChain o înregistrează la crearea contului, iar examples/register-and-send arată cum se înregistrează cu 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 });

Fluxul complet, inclusiv numărul contului, secvența și taxa, se găsește în examples/register-and-send.

5. Alte limbaje: semnarea și verificarea unui mesaj​

Aceste exemple sunt offline. Fiecare derivă cheile din fraza publică de test BIP-39 (adresa qor19rl4cm2hmr8afy4kldpxz3fka4jguq0agt672h), semnează un mesaj cu ambele chei și îl verifică, apoi afișează qor19rl4cm2hmr8afy4kldpxz3fka4jguq0agt672h verified. Nu alimentați niciodată acea frază.

Python​

Nou în 0.1.1. Python 3.9 sau mai nou; wheel-uri pentru macOS și Linux (încă fără wheel pentru Windows).

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")

Acest exemplu a fost rulat cu wheel-urile publicate 0.1.1 pe macOS arm64 și 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)
}

Exemplul Go ignoră erorile ca să rămână scurt; verificați-le în codul real. Rust, Swift și Kotlin urmează același flux, cu aceleași nume de funcții în stilul propriu fiecărui limbaj (vezi prezentarea generală). Artefactele Kotlin și Android au nevoie de compilatorul Kotlin 2.3 sau mai nou: un proiect pe Kotlin 2.1 nu se compilează împotriva lor.

6. Citirea unei tranzacții confirmate (inspectorul de tranzacții)​

Nou în 0.1.1, TypeScript. Doar citește și nu trimite nimic. Raportează cum a fost protejată o tranzacție confirmată: mesajele, semnatarul, taxa, dacă semnătura clasică se verifică, starea stratului ML-DSA-87 și nivelul de securitate (pqc, enrollment, shake sau classical). Vezi Inspectorul de tranzacții pentru câmpuri.

// 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>

Folosiți hash-ul unei tranzacții recente de testnet, de exemplu un transfer al faucet-ului. Rulat astfel, a afișat ML-DSA-87 key registered: true, nivelul pqc, pqLayer { present: true, count: 1, algorithmId: 1, verified: true, signBytesVersion: 'v2' }, mesajul /cosmos.bank.v1beta1.MsgSend și EVM window: false false. inspectTransaction returnează undefined pentru un hash pe care rețeaua nu-l cunoaște. Tranzacțiile vechi de pe testnet pot lipsi, deoarece nodurile de testnet elimină istoricul (pruning).

Mai departe​