diff --git a/api/rpc/contracts.mdx b/api/rpc/contracts.mdx index 17b9618d2e1..e897984a247 100644 --- a/api/rpc/contracts.mdx +++ b/api/rpc/contracts.mdx @@ -12,6 +12,8 @@ The RPC API enables you to view details about accounts and contracts as well as |--------|---------| | [`view_account`](#view-account) | Get basic account information | | [`view_account_changes`](#view-account-changes) | Monitor account state changes | +| [`view_access_key`](#view-access-key) | Get the nonce and permissions of a single access key | +| [`view_access_key_list`](#view-access-key-list) | List all access keys on an account | | [`view_code`](#view-contract-code) | Get deployed contract WASM code | | [`view_state`](#view-contract-state) | Get contract storage data | | [`data_changes`](#view-contract-state-changes) | Monitor contract state changes | @@ -128,6 +130,164 @@ Returns account changes from transactions in a given account. --- +## View Access Key + +Returns the nonce and permissions of a single [access key](/protocol/accounts-contracts/access-keys), given the account and the key's public key. + +- **method**: `query` +- **params**: `request_type: view_access_key`, `finality` OR `block_id`, `account_id`, `public_key` + + + + ```json + { + "jsonrpc": "2.0", + "id": "dontcare", + "method": "query", + "params": { + "request_type": "view_access_key", + "finality": "final", + "account_id": "account.rpc-examples.testnet", + "public_key": "ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa" + } + } + ``` + + + ```js + import { JsonRpcProvider } from "near-api-js"; + + const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); + + const response = await provider.query({ + request_type: 'view_access_key', + finality: 'final', + account_id: 'account.rpc-examples.testnet', + public_key: 'ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa', + }); + ``` + + + ```bash + http POST https://rpc.testnet.near.org \ + jsonrpc=2.0 id=dontcare method=query \ + params:='{"request_type":"view_access_key","finality":"final","account_id":"account.rpc-examples.testnet","public_key":"ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa"}' + ``` + + + + +```json +{ + "jsonrpc": "2.0", + "result": { + "nonce": 187310139000001, + "permission": "FullAccess", + "block_hash": "56xEo2LorUFVNbkFhCncFSWNiobdp1kzm14nZ47b5JVW", + "block_height": 187440904 + }, + "id": "dontcare" +} +``` + +For a function-call key, `permission` is an object instead of the `"FullAccess"` string: + +```json +"permission": { + "FunctionCall": { + "allowance": "250000000000000000000000", + "receiver_id": "contract.rpc-examples.testnet", + "method_names": ["get_greeting"] + } +} +``` + + + +For a post-quantum [`ml-dsa-65`](/protocol/accounts-contracts/access-keys#signature-schemes) key, pass the **full** `ml-dsa-65:` public key in `public_key`. The network hashes it to locate the on-chain entry, so you query it exactly like an `ed25519` or `secp256k1` key. + + +--- + +## View Access Key List + +Returns all [access keys](/protocol/accounts-contracts/access-keys) for an account, each with its nonce and permissions. + +- **method**: `query` +- **params**: `request_type: view_access_key_list`, `finality` OR `block_id`, `account_id` + + + + ```json + { + "jsonrpc": "2.0", + "id": "dontcare", + "method": "query", + "params": { + "request_type": "view_access_key_list", + "finality": "final", + "account_id": "account.rpc-examples.testnet" + } + } + ``` + + + ```js + import { JsonRpcProvider } from "near-api-js"; + + const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); + + const response = await provider.query({ + request_type: 'view_access_key_list', + finality: 'final', + account_id: 'account.rpc-examples.testnet', + }); + ``` + + + ```bash + http POST https://rpc.testnet.near.org \ + jsonrpc=2.0 id=dontcare method=query \ + params:='{"request_type":"view_access_key_list","finality":"final","account_id":"account.rpc-examples.testnet"}' + ``` + + + + +```json +{ + "jsonrpc": "2.0", + "result": { + "keys": [ + { + "public_key": "ed25519:DCkbTMS8s4kqV4nGvz5kRBdNbY9YwjjvNGNXKpAg2eFa", + "access_key": { + "nonce": 187310139000001, + "permission": "FullAccess" + } + }, + { + "public_key": "ml-dsa-65-hash:7Xx2X4vHb9dG8pJ5tZqD3mN6kRfWcA1sLuYoP2eB4hT", + "access_key": { + "nonce": 187420021000003, + "permission": "FullAccess" + } + } + ], + "block_hash": "56xEo2LorUFVNbkFhCncFSWNiobdp1kzm14nZ47b5JVW", + "block_height": 187440904 + }, + "id": "dontcare" +} +``` + + + +Post-quantum [`ml-dsa-65`](/protocol/accounts-contracts/access-keys#signature-schemes) keys are stored on-chain by hash, so they appear here with an `ml-dsa-65-hash:` prefix (a base58-encoded 48-byte SHA3-384 digest) rather than the full `ml-dsa-65:` key. To match an entry to one of your own keys, hash your public key and compare it against the returned value. + + +--- + ## View Contract Code Returns the contract code (Wasm binary) deployed to the account. The returned code is encoded in base64. diff --git a/protocol/accounts-contracts/access-keys.mdx b/protocol/accounts-contracts/access-keys.mdx index 0bb855e4e23..da1756348f8 100644 --- a/protocol/accounts-contracts/access-keys.mdx +++ b/protocol/accounts-contracts/access-keys.mdx @@ -58,6 +58,44 @@ You should never share your `Full-Access`, otherwise you are giving **total cont --- +## Signature Schemes + +Independently of its permission level, every access key is a cryptographic key pair belonging to one of NEAR's supported **signature schemes**. A public key is written as `:`, where the prefix identifies the scheme — for example `ed25519:CQLP1o1F3Jbdttek3GoRJYhzfT...`. + +| Scheme | Public key prefix | Public key size | Quantum-resistant | +| --------- | ----------------- | --------------- | ----------------- | +| Ed25519 | `ed25519:` | 32 bytes | No | +| secp256k1 | `secp256k1:` | 64 bytes | No | +| ML-DSA-65 | `ml-dsa-65:` | 1952 bytes | Yes | + +`ed25519` is the default scheme, used by most wallets and tooling and by NEAR [implicit accounts](./account-id#implicit-address). `secp256k1` is used mainly for [chain signatures](/chain-abstraction/chain-signatures) and Ethereum-compatible flows. A single account can hold keys from different schemes at the same time. + +### Post-Quantum Keys (ML-DSA-65) + +`ml-dsa-65` is a **post-quantum** signature scheme, standardized by NIST as [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final) (Module-Lattice-Based Digital Signature Algorithm, security category 3). Unlike `ed25519` and `secp256k1` — whose security a large enough quantum computer could break — `ml-dsa-65` is designed to stay secure against quantum attacks, so an account protected by an `ml-dsa-65` key cannot be taken over by forging its signature. + +You add and use an `ml-dsa-65` key exactly like any other key: it can be a [full-access](#full-access-keys) or a [function-call](#function-call-keys) key, and it signs transactions the same way. + + +**Post-quantum keys are stored by hash** + +An `ml-dsa-65` public key is large — **1952 bytes**, versus 32 for `ed25519` — and its signatures are **3309 bytes**. To keep accounts cheap to store, NEAR does **not** keep the full public key on-chain; instead it stores a 48-byte [SHA3-384](https://en.wikipedia.org/wiki/SHA-3) hash of it, which keeps the per-key [storage cost](/protocol/storage/storage-staking) close to that of a classical key. + + +Because only the hash is stored, listing an account's keys returns the **hash**, not the full key, for `ml-dsa-65` entries. When you query an account's keys (for example with [`view_access_key_list`](/api/rpc/contracts#view-access-key-list)), `ml-dsa-65` keys appear with an `ml-dsa-65-hash:` prefix instead of `ml-dsa-65:`: + +``` +ml-dsa-65-hash:7Xx2X... # base58-encoded 48-byte SHA3-384 digest +``` + +To recognize one of your own post-quantum keys in such a list, hash your known public key and compare it against the returned value. To look up a specific key directly with [`view_access_key`](/api/rpc/contracts#view-access-key), pass the **full** `ml-dsa-65:` public key and the network hashes it for you. + + +Post-quantum support currently covers **transaction signing and access keys**. Validator (staking) keys, block production, and [implicit account](./account-id#implicit-address) addresses continue to use `ed25519`. + + +--- + ## Limited Access Key Caveats ### Account with Only Function-Call Keys diff --git a/smart-contracts/anatomy/actions.mdx b/smart-contracts/anatomy/actions.mdx index 6f6d4deed9e..64bef85eb6d 100644 --- a/smart-contracts/anatomy/actions.mdx +++ b/smart-contracts/anatomy/actions.mdx @@ -593,6 +593,10 @@ class ExampleContract(Contract): Notice that what you actually add is a "public key". Whoever holds its private counterpart, i.e. the private-key, will be able to use the newly access key. + +The `public_key` can use any of NEAR's [signature schemes](/protocol/accounts-contracts/access-keys#signature-schemes) — `ed25519`, `secp256k1`, or the post-quantum `ml-dsa-65`. Adding an `ml-dsa-65` key needs no code changes: pass an `ml-dsa-65:...` key exactly like the examples above. + + If an account with a contract deployed does **not** have any access keys, this is known as a locked contract. When the account is locked, it cannot sign transactions therefore, actions can **only** be performed from **within** the contract code. diff --git a/smart-contracts/anatomy/environment.mdx b/smart-contracts/anatomy/environment.mdx index 27b22abf349..8975d166d39 100644 --- a/smart-contracts/anatomy/environment.mdx +++ b/smart-contracts/anatomy/environment.mdx @@ -119,6 +119,10 @@ During a simple transaction (no [cross-contract calls](../anatomy/crosscontract) In most scenarios you will **only need to know the predecessor**. However, there are situations in which the signer is very useful. For example, when adding [NFTs](../../primitives/nft/nft) into [this marketplace](https://github.com/near-examples/nft-tutorial/blob/7fb267b83899d1f65f1bceb71804430fab62c7a7/market-contract/src/nft_callbacks.rs#L42), the contract checks that the `signer`, i.e. the person who generated the transaction chain, is the NFT owner. + +`signer_account_pk` returns the signer's public key in the same `:` format used elsewhere. If the transaction was signed with a post-quantum [`ml-dsa-65`](/protocol/accounts-contracts/access-keys#signature-schemes) key, the returned key is correspondingly larger (1952 bytes) — keep that in mind if your contract stores or compares signer public keys. + + --- ## Balances and Attached NEAR diff --git a/tools/cli.mdx b/tools/cli.mdx index c096dcbcbf6..043f1d75790 100644 --- a/tools/cli.mdx +++ b/tools/cli.mdx @@ -185,7 +185,36 @@ near account export-account $ACCOUNT_ID using-web-wallet network-config testnet ## Keys -Showing, adding and removing account keys. +Generating, showing, adding and removing account keys. NEAR keys can use any of the supported [signature schemes](/protocol/accounts-contracts/access-keys#signature-schemes): `ed25519` (default), `secp256k1`, or the post-quantum `ml-dsa-65`. + +### Generate a key pair + +`generate-keypair` - create a fresh key pair offline, without touching an account or the network. Use `--signature-scheme` to choose the scheme (defaults to `ed25519`). + + + + ```bash + # Print a new key pair to the terminal + near generate-keypair print-to-terminal + + # ...or save it to a JSON file + near generate-keypair save-to-file ./my-key.json + ``` + + + ```bash + # Print a new post-quantum key pair to the terminal + near generate-keypair --signature-scheme ml-dsa-65 print-to-terminal + + # ...or save it to a JSON file + near generate-keypair --signature-scheme ml-dsa-65 save-to-file ./pq-key.json + ``` + + + + +Run in an interactive terminal without `--signature-scheme` and the CLI prompts you to pick a scheme; otherwise it defaults to `ed25519`. Unlike `ed25519`, `ml-dsa-65` key pairs are generated randomly and have no seed phrase or implicit-account address — be sure to back up the saved key file. + ### List keys @@ -225,6 +254,27 @@ Showing, adding and removing account keys. +Instead of providing a key, you can have the CLI **autogenerate** one and add it in a single step with `autogenerate-new-keypair --signature-scheme `: + + + + ```bash + export ACCOUNT_ID=bob.testnet + near account add-key $ACCOUNT_ID grant-full-access autogenerate-new-keypair save-to-keychain network-config testnet sign-with-keychain send + ``` + + + ```bash + export ACCOUNT_ID=bob.testnet + near account add-key $ACCOUNT_ID grant-full-access autogenerate-new-keypair --signature-scheme ml-dsa-65 save-to-keychain network-config testnet sign-with-keychain send + ``` + + + + +The same `--signature-scheme` flag is available on the `account create-account fund-myself` and `sponsor-by-faucet-service` autogenerate flows. You can also add an externally generated `ml-dsa-65:` key through the `use-manually-provided-public-key` flow shown above. Post-quantum keys can be added to **named** accounts only — they have no implicit-account form. + + ### Delete key `delete-keys` - delete an access key from an account. @@ -244,6 +294,10 @@ Showing, adding and removing account keys. + +The interactive key picker only lists `ed25519` and `secp256k1` keys. A post-quantum `ml-dsa-65` key is stored on-chain only as a hash, so its full public key can't be read back from the network — to delete one, pass the full `ml-dsa-65:` key explicitly with `public-keys`, exactly as in the example above. + + ## Tokens This will allow you to manage your token assets such as NEAR, FTs and NFTs.