@geeqdev/geeq-sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/geeq_sdk.iml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.nvmrc +1 -0
- package/CONTRIBUTING.md +20 -0
- package/dist/src/address/record-type.d.ts +10 -0
- package/dist/src/address/record-type.js +59 -0
- package/dist/src/address/record-type.js.map +1 -0
- package/dist/src/address/wallet-address.d.ts +34 -0
- package/dist/src/address/wallet-address.js +90 -0
- package/dist/src/address/wallet-address.js.map +1 -0
- package/dist/src/amount.d.ts +43 -0
- package/dist/src/amount.js +105 -0
- package/dist/src/amount.js.map +1 -0
- package/dist/src/assert.d.ts +12 -0
- package/dist/src/assert.js +24 -0
- package/dist/src/assert.js.map +1 -0
- package/dist/src/capnp/adminNetTx.capnp.d.ts +75 -0
- package/dist/src/capnp/adminNetTx.capnp.js +66 -0
- package/dist/src/capnp/adminNetTx.capnp.js.map +1 -0
- package/dist/src/capnp/appLayBlock.capnp.d.ts +26 -0
- package/dist/src/capnp/appLayBlock.capnp.js +24 -0
- package/dist/src/capnp/appLayBlock.capnp.js.map +1 -0
- package/dist/src/capnp/appLayLedger.capnp.d.ts +34 -0
- package/dist/src/capnp/appLayLedger.capnp.js +32 -0
- package/dist/src/capnp/appLayLedger.capnp.js.map +1 -0
- package/dist/src/capnp/appLayRec.capnp.d.ts +129 -0
- package/dist/src/capnp/appLayRec.capnp.js +156 -0
- package/dist/src/capnp/appLayRec.capnp.js.map +1 -0
- package/dist/src/capnp/chainStateView.capnp.d.ts +50 -0
- package/dist/src/capnp/chainStateView.capnp.js +48 -0
- package/dist/src/capnp/chainStateView.capnp.js.map +1 -0
- package/dist/src/capnp/coinAcctRec.capnp.d.ts +178 -0
- package/dist/src/capnp/coinAcctRec.capnp.js +189 -0
- package/dist/src/capnp/coinAcctRec.capnp.js.map +1 -0
- package/dist/src/capnp/genesisBlock.capnp.d.ts +61 -0
- package/dist/src/capnp/genesisBlock.capnp.js +55 -0
- package/dist/src/capnp/genesisBlock.capnp.js.map +1 -0
- package/dist/src/capnp/hubContentStmt.capnp.d.ts +63 -0
- package/dist/src/capnp/hubContentStmt.capnp.js +57 -0
- package/dist/src/capnp/hubContentStmt.capnp.js.map +1 -0
- package/dist/src/capnp/misc.capnp.d.ts +20 -0
- package/dist/src/capnp/misc.capnp.js +18 -0
- package/dist/src/capnp/misc.capnp.js.map +1 -0
- package/dist/src/capnp/netActorRec.capnp.d.ts +65 -0
- package/dist/src/capnp/netActorRec.capnp.js +65 -0
- package/dist/src/capnp/netActorRec.capnp.js.map +1 -0
- package/dist/src/capnp/nodeContentStmt.capnp.d.ts +67 -0
- package/dist/src/capnp/nodeContentStmt.capnp.js +61 -0
- package/dist/src/capnp/nodeContentStmt.capnp.js.map +1 -0
- package/dist/src/capnp/nodePayloadList.capnp.d.ts +28 -0
- package/dist/src/capnp/nodePayloadList.capnp.js +26 -0
- package/dist/src/capnp/nodePayloadList.capnp.js.map +1 -0
- package/dist/src/capnp/nodeTxList.capnp.d.ts +36 -0
- package/dist/src/capnp/nodeTxList.capnp.js +34 -0
- package/dist/src/capnp/nodeTxList.capnp.js.map +1 -0
- package/dist/src/capnp/paramAcctRec.capnp.d.ts +338 -0
- package/dist/src/capnp/paramAcctRec.capnp.js +669 -0
- package/dist/src/capnp/paramAcctRec.capnp.js.map +1 -0
- package/dist/src/capnp/payload.capnp.d.ts +237 -0
- package/dist/src/capnp/payload.capnp.js +260 -0
- package/dist/src/capnp/payload.capnp.js.map +1 -0
- package/dist/src/capnp/proxyAcctRec.capnp.d.ts +223 -0
- package/dist/src/capnp/proxyAcctRec.capnp.js +255 -0
- package/dist/src/capnp/proxyAcctRec.capnp.js.map +1 -0
- package/dist/src/capnp/relayContentStmt.capnp.d.ts +82 -0
- package/dist/src/capnp/relayContentStmt.capnp.js +85 -0
- package/dist/src/capnp/relayContentStmt.capnp.js.map +1 -0
- package/dist/src/capnp/sysAcctRec.capnp.d.ts +48 -0
- package/dist/src/capnp/sysAcctRec.capnp.js +46 -0
- package/dist/src/capnp/sysAcctRec.capnp.js.map +1 -0
- package/dist/src/capnp/userAppTx.capnp.d.ts +230 -0
- package/dist/src/capnp/userAppTx.capnp.js +270 -0
- package/dist/src/capnp/userAppTx.capnp.js.map +1 -0
- package/dist/src/capnp/userValTx.capnp.d.ts +1575 -0
- package/dist/src/capnp/userValTx.capnp.js +2004 -0
- package/dist/src/capnp/userValTx.capnp.js.map +1 -0
- package/dist/src/capnp/valLayBlock.capnp.d.ts +88 -0
- package/dist/src/capnp/valLayBlock.capnp.js +86 -0
- package/dist/src/capnp/valLayBlock.capnp.js.map +1 -0
- package/dist/src/capnp/valLayLedger.capnp.d.ts +85 -0
- package/dist/src/capnp/valLayLedger.capnp.js +75 -0
- package/dist/src/capnp/valLayLedger.capnp.js.map +1 -0
- package/dist/src/chain/geeq-chain.d.ts +474 -0
- package/dist/src/chain/geeq-chain.js +721 -0
- package/dist/src/chain/geeq-chain.js.map +1 -0
- package/dist/src/chain/geeq-node.d.ts +57 -0
- package/dist/src/chain/geeq-node.js +62 -0
- package/dist/src/chain/geeq-node.js.map +1 -0
- package/dist/src/chain/nonce-guard.d.ts +42 -0
- package/dist/src/chain/nonce-guard.js +68 -0
- package/dist/src/chain/nonce-guard.js.map +1 -0
- package/dist/src/chain/select.d.ts +40 -0
- package/dist/src/chain/select.js +114 -0
- package/dist/src/chain/select.js.map +1 -0
- package/dist/src/crypto/account-id.d.ts +35 -0
- package/dist/src/crypto/account-id.js +58 -0
- package/dist/src/crypto/account-id.js.map +1 -0
- package/dist/src/crypto/keypair.d.ts +24 -0
- package/dist/src/crypto/keypair.js +158 -0
- package/dist/src/crypto/keypair.js.map +1 -0
- package/dist/src/crypto/mnemonic.d.ts +34 -0
- package/dist/src/crypto/mnemonic.js +57 -0
- package/dist/src/crypto/mnemonic.js.map +1 -0
- package/dist/src/encoding/base64.d.ts +8 -0
- package/dist/src/encoding/base64.js +53 -0
- package/dist/src/encoding/base64.js.map +1 -0
- package/dist/src/encoding/hex.d.ts +2 -0
- package/dist/src/encoding/hex.js +35 -0
- package/dist/src/encoding/hex.js.map +1 -0
- package/dist/src/errors.d.ts +83 -0
- package/dist/src/errors.js +49 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +59 -0
- package/dist/src/index.js +53 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/ledger/coin-accounts.d.ts +42 -0
- package/dist/src/ledger/coin-accounts.js +37 -0
- package/dist/src/ledger/coin-accounts.js.map +1 -0
- package/dist/src/ledger/decode.d.ts +18 -0
- package/dist/src/ledger/decode.js +113 -0
- package/dist/src/ledger/decode.js.map +1 -0
- package/dist/src/ledger/geeq-ledger.d.ts +99 -0
- package/dist/src/ledger/geeq-ledger.js +125 -0
- package/dist/src/ledger/geeq-ledger.js.map +1 -0
- package/dist/src/ledger/net-actors.d.ts +65 -0
- package/dist/src/ledger/net-actors.js +53 -0
- package/dist/src/ledger/net-actors.js.map +1 -0
- package/dist/src/ledger/nft-records.d.ts +13 -0
- package/dist/src/ledger/nft-records.js +41 -0
- package/dist/src/ledger/nft-records.js.map +1 -0
- package/dist/src/ledger/params.d.ts +34 -0
- package/dist/src/ledger/params.js +71 -0
- package/dist/src/ledger/params.js.map +1 -0
- package/dist/src/ledger/proxy-accounts.d.ts +26 -0
- package/dist/src/ledger/proxy-accounts.js +27 -0
- package/dist/src/ledger/proxy-accounts.js.map +1 -0
- package/dist/src/net/discovery.d.ts +62 -0
- package/dist/src/net/discovery.js +136 -0
- package/dist/src/net/discovery.js.map +1 -0
- package/dist/src/net/explorer.d.ts +57 -0
- package/dist/src/net/explorer.js +64 -0
- package/dist/src/net/explorer.js.map +1 -0
- package/dist/src/net/read-ledger.d.ts +7 -0
- package/dist/src/net/read-ledger.js +39 -0
- package/dist/src/net/read-ledger.js.map +1 -0
- package/dist/src/net/routes.d.ts +49 -0
- package/dist/src/net/routes.js +70 -0
- package/dist/src/net/routes.js.map +1 -0
- package/dist/src/net/transport.d.ts +55 -0
- package/dist/src/net/transport.js +168 -0
- package/dist/src/net/transport.js.map +1 -0
- package/dist/src/net/websocket.d.ts +49 -0
- package/dist/src/net/websocket.js +54 -0
- package/dist/src/net/websocket.js.map +1 -0
- package/dist/src/runtime/bindings.d.ts +9 -0
- package/dist/src/runtime/bindings.js +27 -0
- package/dist/src/runtime/bindings.js.map +1 -0
- package/dist/src/runtime/init.d.ts +13 -0
- package/dist/src/runtime/init.js +40 -0
- package/dist/src/runtime/init.js.map +1 -0
- package/dist/src/runtime/wasm-loader.browser.d.ts +14 -0
- package/dist/src/runtime/wasm-loader.browser.js +20 -0
- package/dist/src/runtime/wasm-loader.browser.js.map +1 -0
- package/dist/src/runtime/wasm-loader.node.d.ts +7 -0
- package/dist/src/runtime/wasm-loader.node.js +14 -0
- package/dist/src/runtime/wasm-loader.node.js.map +1 -0
- package/dist/src/socket/geeq-socket.d.ts +232 -0
- package/dist/src/socket/geeq-socket.js +634 -0
- package/dist/src/socket/geeq-socket.js.map +1 -0
- package/dist/src/socket/tracked-tx.d.ts +148 -0
- package/dist/src/socket/tracked-tx.js +212 -0
- package/dist/src/socket/tracked-tx.js.map +1 -0
- package/dist/src/socket/tx-status.d.ts +69 -0
- package/dist/src/socket/tx-status.js +72 -0
- package/dist/src/socket/tx-status.js.map +1 -0
- package/dist/src/tx/attestation.d.ts +60 -0
- package/dist/src/tx/attestation.js +163 -0
- package/dist/src/tx/attestation.js.map +1 -0
- package/dist/src/tx/content.d.ts +27 -0
- package/dist/src/tx/content.js +62 -0
- package/dist/src/tx/content.js.map +1 -0
- package/dist/src/tx/encode.d.ts +60 -0
- package/dist/src/tx/encode.js +82 -0
- package/dist/src/tx/encode.js.map +1 -0
- package/dist/src/tx/expiry.d.ts +11 -0
- package/dist/src/tx/expiry.js +12 -0
- package/dist/src/tx/expiry.js.map +1 -0
- package/dist/src/tx/fee.d.ts +16 -0
- package/dist/src/tx/fee.js +29 -0
- package/dist/src/tx/fee.js.map +1 -0
- package/dist/src/tx/metadata.d.ts +39 -0
- package/dist/src/tx/metadata.js +66 -0
- package/dist/src/tx/metadata.js.map +1 -0
- package/dist/src/tx/nft.d.ts +16 -0
- package/dist/src/tx/nft.js +98 -0
- package/dist/src/tx/nft.js.map +1 -0
- package/dist/src/tx/proxy.d.ts +37 -0
- package/dist/src/tx/proxy.js +134 -0
- package/dist/src/tx/proxy.js.map +1 -0
- package/dist/src/tx/transfer.d.ts +31 -0
- package/dist/src/tx/transfer.js +123 -0
- package/dist/src/tx/transfer.js.map +1 -0
- package/dist/src/types.d.ts +448 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tests/account-id.test.d.ts +1 -0
- package/dist/tests/account-id.test.js +27 -0
- package/dist/tests/account-id.test.js.map +1 -0
- package/dist/tests/amount.test.d.ts +1 -0
- package/dist/tests/amount.test.js +113 -0
- package/dist/tests/amount.test.js.map +1 -0
- package/dist/tests/attestation.test.d.ts +1 -0
- package/dist/tests/attestation.test.js +182 -0
- package/dist/tests/attestation.test.js.map +1 -0
- package/dist/tests/chain.test.d.ts +1 -0
- package/dist/tests/chain.test.js +647 -0
- package/dist/tests/chain.test.js.map +1 -0
- package/dist/tests/content.test.d.ts +1 -0
- package/dist/tests/content.test.js +90 -0
- package/dist/tests/content.test.js.map +1 -0
- package/dist/tests/documented-api.test.d.ts +1 -0
- package/dist/tests/documented-api.test.js +229 -0
- package/dist/tests/documented-api.test.js.map +1 -0
- package/dist/tests/explorer.test.d.ts +1 -0
- package/dist/tests/explorer.test.js +95 -0
- package/dist/tests/explorer.test.js.map +1 -0
- package/dist/tests/fee.test.d.ts +1 -0
- package/dist/tests/fee.test.js +179 -0
- package/dist/tests/fee.test.js.map +1 -0
- package/dist/tests/fixtures/build-vll.d.ts +71 -0
- package/dist/tests/fixtures/build-vll.js +141 -0
- package/dist/tests/fixtures/build-vll.js.map +1 -0
- package/dist/tests/fixtures/fake-socket.d.ts +44 -0
- package/dist/tests/fixtures/fake-socket.js +93 -0
- package/dist/tests/fixtures/fake-socket.js.map +1 -0
- package/dist/tests/fixtures/fake-transport.d.ts +24 -0
- package/dist/tests/fixtures/fake-transport.js +30 -0
- package/dist/tests/fixtures/fake-transport.js.map +1 -0
- package/dist/tests/keypair.test.d.ts +1 -0
- package/dist/tests/keypair.test.js +163 -0
- package/dist/tests/keypair.test.js.map +1 -0
- package/dist/tests/ledger.test.d.ts +1 -0
- package/dist/tests/ledger.test.js +234 -0
- package/dist/tests/ledger.test.js.map +1 -0
- package/dist/tests/mnemonic.test.d.ts +1 -0
- package/dist/tests/mnemonic.test.js +97 -0
- package/dist/tests/mnemonic.test.js.map +1 -0
- package/dist/tests/nft-transfer.test.d.ts +1 -0
- package/dist/tests/nft-transfer.test.js +170 -0
- package/dist/tests/nft-transfer.test.js.map +1 -0
- package/dist/tests/nft.test.d.ts +1 -0
- package/dist/tests/nft.test.js +180 -0
- package/dist/tests/nft.test.js.map +1 -0
- package/dist/tests/offline.test.d.ts +1 -0
- package/dist/tests/offline.test.js +95 -0
- package/dist/tests/offline.test.js.map +1 -0
- package/dist/tests/proxy.test.d.ts +1 -0
- package/dist/tests/proxy.test.js +183 -0
- package/dist/tests/proxy.test.js.map +1 -0
- package/dist/tests/select.test.d.ts +1 -0
- package/dist/tests/select.test.js +200 -0
- package/dist/tests/select.test.js.map +1 -0
- package/dist/tests/socket.test.d.ts +1 -0
- package/dist/tests/socket.test.js +607 -0
- package/dist/tests/socket.test.js.map +1 -0
- package/dist/tests/transfer.test.d.ts +1 -0
- package/dist/tests/transfer.test.js +225 -0
- package/dist/tests/transfer.test.js.map +1 -0
- package/dist/tests/wallet-address.test.d.ts +1 -0
- package/dist/tests/wallet-address.test.js +101 -0
- package/dist/tests/wallet-address.test.js.map +1 -0
- package/dist/tests/wasm-loader.jest.d.ts +13 -0
- package/dist/tests/wasm-loader.jest.js +15 -0
- package/dist/tests/wasm-loader.jest.js.map +1 -0
- package/example/.env.example +4 -0
- package/example/README.md +304 -0
- package/example/USAGE.md +74 -0
- package/example/index.html +12 -0
- package/example/package-lock.json +1557 -0
- package/example/package.json +25 -0
- package/example/scripts/browser-check.mjs +1421 -0
- package/example/scripts/match-derivation.mjs +65 -0
- package/example/src/App.vue +138 -0
- package/example/src/components/AuthorityPanel.vue +277 -0
- package/example/src/components/ChainPanel.vue +267 -0
- package/example/src/components/ChainState.vue +59 -0
- package/example/src/components/CopyButton.vue +25 -0
- package/example/src/components/CreateAssetPanel.vue +313 -0
- package/example/src/components/DigitalAssetsPanel.vue +68 -0
- package/example/src/components/EvidencePanel.vue +396 -0
- package/example/src/components/ExplorerPanel.vue +119 -0
- package/example/src/components/KeypairPanel.vue +182 -0
- package/example/src/components/PhrasePanel.vue +105 -0
- package/example/src/components/SocketWarning.vue +18 -0
- package/example/src/components/StepGate.vue +24 -0
- package/example/src/components/TransferPanel.vue +416 -0
- package/example/src/components/TxDeadline.vue +19 -0
- package/example/src/components/TxFee.vue +31 -0
- package/example/src/components/TxStatus.vue +105 -0
- package/example/src/composables/useSocketConnection.ts +80 -0
- package/example/src/env.d.ts +12 -0
- package/example/src/main.ts +5 -0
- package/example/src/pennies.ts +7 -0
- package/example/src/style.css +149 -0
- package/example/tsconfig.json +19 -0
- package/example/vite.config.ts +26 -0
- package/jest.config.js +18 -0
- package/package.json +2 -6
- package/scripts/copy-wasm.mjs +21 -0
- package/scripts/smoke.mjs +382 -0
- package/src/address/record-type.ts +77 -0
- package/src/address/wallet-address.ts +105 -0
- package/src/amount.ts +143 -0
- package/src/assert.ts +30 -0
- package/src/capnp/README.md +15 -0
- package/src/capnp/adminNetTx.capnp.ts +66 -0
- package/src/capnp/appLayBlock.capnp.ts +26 -0
- package/src/capnp/appLayLedger.capnp.ts +35 -0
- package/src/capnp/appLayRec.capnp.ts +159 -0
- package/src/capnp/chainStateView.capnp.ts +49 -0
- package/src/capnp/coinAcctRec.capnp.ts +193 -0
- package/src/capnp/genesisBlock.capnp.ts +56 -0
- package/src/capnp/hubContentStmt.capnp.ts +59 -0
- package/src/capnp/misc.capnp.ts +19 -0
- package/src/capnp/netActorRec.capnp.ts +64 -0
- package/src/capnp/nodeContentStmt.capnp.ts +62 -0
- package/src/capnp/nodePayloadList.capnp.ts +28 -0
- package/src/capnp/nodeTxList.capnp.ts +37 -0
- package/src/capnp/paramAcctRec.capnp.ts +669 -0
- package/src/capnp/payload.capnp.ts +261 -0
- package/src/capnp/proxyAcctRec.capnp.ts +259 -0
- package/src/capnp/relayContentStmt.capnp.ts +85 -0
- package/src/capnp/sysAcctRec.capnp.ts +47 -0
- package/src/capnp/userAppTx.capnp.ts +269 -0
- package/src/capnp/userValTx.capnp.ts +1999 -0
- package/src/capnp/valLayBlock.capnp.ts +93 -0
- package/src/capnp/valLayLedger.capnp.ts +80 -0
- package/src/chain/geeq-chain.ts +1094 -0
- package/src/chain/geeq-node.ts +105 -0
- package/src/chain/nonce-guard.ts +73 -0
- package/src/chain/select.ts +149 -0
- package/src/crypto/account-id.ts +83 -0
- package/src/crypto/keypair.ts +221 -0
- package/src/crypto/mnemonic.ts +69 -0
- package/src/encoding/base64.ts +58 -0
- package/src/encoding/hex.ts +42 -0
- package/src/errors.ts +122 -0
- package/src/index.ts +195 -0
- package/src/ledger/coin-accounts.ts +73 -0
- package/src/ledger/decode.ts +147 -0
- package/src/ledger/geeq-ledger.ts +184 -0
- package/src/ledger/net-actors.ts +129 -0
- package/src/ledger/nft-records.ts +49 -0
- package/src/ledger/params.ts +82 -0
- package/src/ledger/proxy-accounts.ts +51 -0
- package/src/net/discovery.ts +231 -0
- package/src/net/explorer.ts +123 -0
- package/src/net/read-ledger.ts +58 -0
- package/src/net/routes.ts +79 -0
- package/src/net/transport.ts +221 -0
- package/src/net/websocket.ts +94 -0
- package/src/runtime/bindings.ts +38 -0
- package/src/runtime/init.ts +49 -0
- package/src/runtime/wasm-loader.browser.ts +22 -0
- package/src/runtime/wasm-loader.node.ts +16 -0
- package/src/socket/geeq-socket.ts +827 -0
- package/src/socket/tracked-tx.ts +319 -0
- package/src/socket/tx-status.ts +131 -0
- package/src/tx/attestation.ts +222 -0
- package/src/tx/content.ts +82 -0
- package/src/tx/encode.ts +107 -0
- package/src/tx/expiry.ts +11 -0
- package/src/tx/fee.ts +31 -0
- package/src/tx/metadata.ts +100 -0
- package/src/tx/nft.ts +108 -0
- package/src/tx/proxy.ts +156 -0
- package/src/tx/transfer.ts +162 -0
- package/src/types.ts +503 -0
- package/src/wasm/README.md +22 -0
- package/src/wasm/browser/geeq_core.d.ts +1224 -0
- package/src/wasm/browser/geeq_core.js +6014 -0
- package/src/wasm/browser/geeq_core_bg.wasm +0 -0
- package/src/wasm/browser/geeq_core_bg.wasm.d.ts +447 -0
- package/src/wasm/nodejs/geeq_core.d.ts +753 -0
- package/src/wasm/nodejs/geeq_core.js +6016 -0
- package/src/wasm/nodejs/geeq_core_bg.wasm +0 -0
- package/src/wasm/nodejs/geeq_core_bg.wasm.d.ts +447 -0
- package/src/wasm/nodejs/package.json +3 -0
- package/tsconfig.build.json +8 -0
- package/tsconfig.json +21 -0
- package/tsup.config.ts +60 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { GeeqLedger } from '../ledger/geeq-ledger'
|
|
2
|
+
import type { GeeqNodeRole, GeeqNodeStatus } from '../ledger/net-actors'
|
|
3
|
+
import { readLedger } from '../net/read-ledger'
|
|
4
|
+
import { submitRoute } from '../net/routes'
|
|
5
|
+
import type { GeeqTransport } from '../net/transport'
|
|
6
|
+
|
|
7
|
+
/** @internal */
|
|
8
|
+
export interface NodeRequestConfig {
|
|
9
|
+
readonly transport: GeeqTransport
|
|
10
|
+
readonly timeoutMs: number
|
|
11
|
+
readonly retries: number
|
|
12
|
+
readonly headers: Readonly<Record<string, string>> | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @internal */
|
|
16
|
+
export interface GeeqNodeInit extends NodeRequestConfig {
|
|
17
|
+
readonly url: string
|
|
18
|
+
readonly nodeNumber: number
|
|
19
|
+
readonly host: string
|
|
20
|
+
readonly port: number
|
|
21
|
+
readonly role: GeeqNodeRole
|
|
22
|
+
readonly status: GeeqNodeStatus
|
|
23
|
+
readonly processing: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One node, identified in its own chain's ledger.
|
|
28
|
+
*
|
|
29
|
+
* A node is only meaningful once a ledger has confirmed which actor it is: the number a
|
|
30
|
+
* transaction must carry is the node's own `nodeNumSelf`, checked with strict equality,
|
|
31
|
+
* and nothing outside the ledger reports it.
|
|
32
|
+
*/
|
|
33
|
+
export class GeeqNode {
|
|
34
|
+
/** Absolute origin, no trailing slash. */
|
|
35
|
+
readonly url: string
|
|
36
|
+
/** `nodeNumSelf` — what a transaction targeting this node must name. */
|
|
37
|
+
readonly nodeNumber: number
|
|
38
|
+
/** The bare IP the ledger records for it. */
|
|
39
|
+
readonly host: string
|
|
40
|
+
/** Its own listen port from the ledger, which is not necessarily {@link url}'s. */
|
|
41
|
+
readonly port: number
|
|
42
|
+
readonly role: GeeqNodeRole
|
|
43
|
+
readonly status: GeeqNodeStatus
|
|
44
|
+
/** Whether it accepts transactions at all. */
|
|
45
|
+
readonly processing: boolean
|
|
46
|
+
|
|
47
|
+
// A TypeScript `private`, never a `#private`: a `#private` field is unreachable
|
|
48
|
+
// through a Proxy, which breaks Vue's ref() — the trap already documented on Keypair.
|
|
49
|
+
private readonly request!: NodeRequestConfig
|
|
50
|
+
|
|
51
|
+
/** @internal */
|
|
52
|
+
constructor(init: GeeqNodeInit) {
|
|
53
|
+
this.url = init.url
|
|
54
|
+
this.nodeNumber = init.nodeNumber
|
|
55
|
+
this.host = init.host
|
|
56
|
+
this.port = init.port
|
|
57
|
+
this.role = init.role
|
|
58
|
+
this.status = init.status
|
|
59
|
+
this.processing = init.processing
|
|
60
|
+
Object.defineProperty(this, 'request', {
|
|
61
|
+
value: Object.freeze({
|
|
62
|
+
transport: init.transport,
|
|
63
|
+
timeoutMs: init.timeoutMs,
|
|
64
|
+
retries: init.retries,
|
|
65
|
+
headers: init.headers,
|
|
66
|
+
}),
|
|
67
|
+
enumerable: false,
|
|
68
|
+
writable: false,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Where a transaction built for this node must be POSTed.
|
|
74
|
+
*
|
|
75
|
+
* The SDK does not submit. This exists so an application posts to the node the SDK
|
|
76
|
+
* actually read from, instead of keeping a second copy of the URL that can drift.
|
|
77
|
+
*/
|
|
78
|
+
get submitUrl(): string {
|
|
79
|
+
return submitRoute(this.url)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Read this node's current ledger. No caching — {@link GeeqChain} owns that. */
|
|
83
|
+
async readLedger(options: { readonly signal?: AbortSignal } = {}): Promise<GeeqLedger> {
|
|
84
|
+
return readLedger(this.url, {
|
|
85
|
+
...this.request,
|
|
86
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
toJSON(): Record<string, unknown> {
|
|
91
|
+
return {
|
|
92
|
+
url: this.url,
|
|
93
|
+
nodeNumber: this.nodeNumber,
|
|
94
|
+
host: this.host,
|
|
95
|
+
port: this.port,
|
|
96
|
+
role: this.role,
|
|
97
|
+
status: this.status,
|
|
98
|
+
processing: this.processing,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
toString(): string {
|
|
103
|
+
return `GeeqNode(${this.nodeNumber} at ${this.url}, ${this.role}/${this.status})`
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { nonceCollision } from '../errors'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stops one account producing two transactions against one block.
|
|
5
|
+
*
|
|
6
|
+
* The ledger only reflects committed blocks, so building twice inside a block interval
|
|
7
|
+
* derives the same nonce twice — and the node invalidates **every** transaction in a
|
|
8
|
+
* duplicate-nonce group rather than keeping the first
|
|
9
|
+
* (geeq_node_core/src/validations/workers/joint/checks/9.nonce.ts). The user would see
|
|
10
|
+
* one success and lose both.
|
|
11
|
+
*
|
|
12
|
+
* Auto-incrementing instead was considered and rejected. The node's full check requires
|
|
13
|
+
* nonces consecutive from the ledger's, so handing out n+1 and then submitting only that
|
|
14
|
+
* one leaves a permanent gap. Since this SDK does not submit, it never learns which
|
|
15
|
+
* transactions actually went out, so it cannot safely keep a counter. Refusing turns a
|
|
16
|
+
* silent double loss into an error naming the ways forward — and an application that
|
|
17
|
+
* knows a build was never sent hands its nonce back with {@link release}.
|
|
18
|
+
*/
|
|
19
|
+
export class NonceGuard {
|
|
20
|
+
/** Per account: the ledger block its last build was against, and that build's id. */
|
|
21
|
+
private readonly issued = new Map<string, { block: number; transaction: string }>()
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether claiming now would collide, without recording anything.
|
|
25
|
+
*
|
|
26
|
+
* Lets a caller tell "this account already built against this block" apart from
|
|
27
|
+
* "the ledger we are looking at is simply out of date", which are the same thing from
|
|
28
|
+
* a cached read and very different from a fresh one.
|
|
29
|
+
*/
|
|
30
|
+
wouldCollide(accountKey: string, ledgerBlock: number): boolean {
|
|
31
|
+
const previous = this.issued.get(accountKey)
|
|
32
|
+
return previous !== undefined && previous.block >= ledgerBlock
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Record that `accountKey` is taking a nonce at `ledgerBlock`, for the transaction
|
|
37
|
+
* identified by `transaction`.
|
|
38
|
+
*
|
|
39
|
+
* @throws a `nonce-collision` GeeqError if it already took one at that block.
|
|
40
|
+
*/
|
|
41
|
+
claim(accountKey: string, ledgerBlock: number, nonce: number, transaction: string): void {
|
|
42
|
+
const previous = this.issued.get(accountKey)
|
|
43
|
+
|
|
44
|
+
if (previous !== undefined && previous.block >= ledgerBlock) {
|
|
45
|
+
throw nonceCollision(
|
|
46
|
+
`This account already has a transaction built with nonce ${nonce} against ` +
|
|
47
|
+
`block ${previous.block}, and the ledger has not advanced past it. Two ` +
|
|
48
|
+
'transactions from one account sharing a nonce are both rejected, not just the ' +
|
|
49
|
+
'second. If the first was never sent, discard(tx) it and build again. Otherwise ' +
|
|
50
|
+
'submit it and wait for the next block, pass nonce: ' +
|
|
51
|
+
`${nonce + 1} explicitly if you mean to submit both together, or call ` +
|
|
52
|
+
'refresh() once the chain has moved on.',
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.issued.set(accountKey, { block: ledgerBlock, transaction })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Give back the nonce a transaction claimed, because it will never be sent. Nothing
|
|
61
|
+
* happens unless it is still its account's latest claim.
|
|
62
|
+
*/
|
|
63
|
+
release(transaction: string): void {
|
|
64
|
+
for (const [accountKey, claim] of this.issued) {
|
|
65
|
+
if (claim.transaction === transaction) this.issued.delete(accountKey)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Forget every claim. For a key change, or after abandoning a built transaction. */
|
|
70
|
+
clear(): void {
|
|
71
|
+
this.issued.clear()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { invalidArgument, nodeUnusable } from '../errors'
|
|
2
|
+
import type { GeeqLedger } from '../ledger/geeq-ledger'
|
|
3
|
+
import type { GeeqLedgerNode } from '../ledger/net-actors'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The port every Geeq node answers client traffic on.
|
|
7
|
+
*
|
|
8
|
+
* Nodes terminate TLS on 2082 and that is the only client-facing port; an endpoint
|
|
9
|
+
* given without one is resolved to it rather than to the scheme's default, because a
|
|
10
|
+
* request to 443 would fail opaquely in a browser with no way to tell why.
|
|
11
|
+
*/
|
|
12
|
+
export const GEEQ_NODE_PORT = 2082
|
|
13
|
+
|
|
14
|
+
/** The host part of a node URL, which is what the net actor list records. */
|
|
15
|
+
export function hostOf(url: string | URL): string {
|
|
16
|
+
try {
|
|
17
|
+
return new URL(String(url)).hostname
|
|
18
|
+
} catch (cause) {
|
|
19
|
+
throw invalidArgument(
|
|
20
|
+
`endpoint must be an absolute URL such as https://1.2.3.4:2082, received ${JSON.stringify(String(url))}.`,
|
|
21
|
+
cause,
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Normalise a node URL to an origin with no trailing slash, so it is safe to append to. */
|
|
27
|
+
export function originOf(url: string | URL): string {
|
|
28
|
+
hostOf(url) // same validation, same message
|
|
29
|
+
const parsed = new URL(String(url))
|
|
30
|
+
const port = parsed.port === '' ? String(GEEQ_NODE_PORT) : parsed.port
|
|
31
|
+
return `${parsed.protocol}//${parsed.hostname}:${port}${parsed.pathname.replace(/\/+$/u, '')}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Normalise an ordinary service URL, leaving its port exactly as given.
|
|
36
|
+
*
|
|
37
|
+
* Distinct from {@link originOf} on purpose: a node directory is a normal web service
|
|
38
|
+
* on whatever port it was deployed on, so filling in the Geeq node port would send
|
|
39
|
+
* every request to the wrong place.
|
|
40
|
+
*/
|
|
41
|
+
export function serviceOriginOf(url: string | URL): string {
|
|
42
|
+
hostOf(url)
|
|
43
|
+
const parsed = new URL(String(url))
|
|
44
|
+
return `${parsed.protocol}//${parsed.host}${parsed.pathname.replace(/\/+$/u, '')}`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function listAlternatives(ledger: GeeqLedger): string {
|
|
48
|
+
const processing = ledger.processingNodes.filter((node) => !node.retiring)
|
|
49
|
+
if (processing.length === 0) {
|
|
50
|
+
return 'This ledger lists no node that is currently processing transactions.'
|
|
51
|
+
}
|
|
52
|
+
const listed = processing
|
|
53
|
+
.map((node) => `${node.host} (node ${node.nodeNumber})`)
|
|
54
|
+
.join(', ')
|
|
55
|
+
return `Nodes in this ledger that are processing: ${listed}.`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function assertUsable(node: GeeqLedgerNode, ledger: GeeqLedger): GeeqLedgerNode {
|
|
59
|
+
if (!node.processing) {
|
|
60
|
+
// Name the role and status verbatim: they are what the operator sees too, and the
|
|
61
|
+
// node's own rejection ('not processing') says nothing about which is wrong.
|
|
62
|
+
const detail =
|
|
63
|
+
node.role === 'relay'
|
|
64
|
+
? 'a relay registers no transaction handler at all'
|
|
65
|
+
: 'only anon-node, anchor-node or cert-node with status node or hub processes transactions'
|
|
66
|
+
throw nodeUnusable(
|
|
67
|
+
`Node ${node.nodeNumber} at ${node.host} is a ${node.role} with status ` +
|
|
68
|
+
`"${node.status}", so it will not accept this transaction — ${detail}. ` +
|
|
69
|
+
listAlternatives(ledger),
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (node.retiring) {
|
|
74
|
+
// The node checks its own rotation deadline before accepting anything and answers
|
|
75
|
+
// 503 'Block chain is stopped' once it is within two blocks of it. That is knowable
|
|
76
|
+
// here, and a 503 naming a stopped chain is a badly misleading thing to hand someone
|
|
77
|
+
// whose chain is running perfectly well.
|
|
78
|
+
throw nodeUnusable(
|
|
79
|
+
`Node ${node.nodeNumber} at ${node.host} leaves the active set at block ` +
|
|
80
|
+
`${node.retiresAtBlock} and the ledger is already at ${ledger.blockNumber}, so it ` +
|
|
81
|
+
'will answer 503 "Block chain is stopped" rather than accept a transaction. This ' +
|
|
82
|
+
`is the node rotating out, not the chain halting. ${listAlternatives(ledger)}`,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return node
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface SelectNodeOptions {
|
|
90
|
+
/** Skip host matching entirely. Required behind a gateway or a DNS name. */
|
|
91
|
+
readonly nodeNumber?: number | undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Choose the net actor record that a transaction should name.
|
|
96
|
+
*
|
|
97
|
+
* Matching is on **host only**. A record's `port` is the node's own listen port, while
|
|
98
|
+
* a client reaches it through a proxy on another one, so requiring both to agree finds
|
|
99
|
+
* nothing on a correctly deployed chain. Port is used solely to disambiguate when one
|
|
100
|
+
* address carries several records.
|
|
101
|
+
*
|
|
102
|
+
* This never silently retargets. A different node means a different address, a
|
|
103
|
+
* different certificate, a different CORS origin and a different operator seeing the
|
|
104
|
+
* transaction, so an unusable node is reported with its alternatives and the caller
|
|
105
|
+
* decides.
|
|
106
|
+
*/
|
|
107
|
+
export function selectNode(
|
|
108
|
+
ledger: GeeqLedger,
|
|
109
|
+
endpoint: string | URL,
|
|
110
|
+
options: SelectNodeOptions = {},
|
|
111
|
+
): GeeqLedgerNode {
|
|
112
|
+
if (options.nodeNumber !== undefined) {
|
|
113
|
+
const named = ledger.findNode({ nodeNumber: options.nodeNumber })
|
|
114
|
+
if (named === undefined) {
|
|
115
|
+
throw nodeUnusable(
|
|
116
|
+
`nodeNumber ${options.nodeNumber} is not in the net actor list of the ledger ` +
|
|
117
|
+
`served by ${String(endpoint)}, which lists ${ledger.nodes.length} records. ` +
|
|
118
|
+
listAlternatives(ledger),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
return assertUsable(named, ledger)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const host = hostOf(endpoint)
|
|
125
|
+
const candidates = ledger.findNodes(host)
|
|
126
|
+
|
|
127
|
+
if (candidates.length === 0) {
|
|
128
|
+
// Almost always a hostname or a gateway: the list holds bare IPs and a browser
|
|
129
|
+
// cannot resolve a name to one, so there is nothing the SDK can infer here.
|
|
130
|
+
throw nodeUnusable(
|
|
131
|
+
`${host} is not in the net actor list of the ledger it served ` +
|
|
132
|
+
`(${ledger.nodes.length} records, none at that address). A node is matched by ` +
|
|
133
|
+
'its IP address, so a DNS name, a gateway or a proxy cannot be matched at all — ' +
|
|
134
|
+
`pass nodeNumber explicitly for those. ${listAlternatives(ledger)}`,
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (candidates.length === 1) return assertUsable(candidates[0] as GeeqLedgerNode, ledger)
|
|
139
|
+
|
|
140
|
+
const processing = candidates.filter((node) => node.processing && !node.retiring)
|
|
141
|
+
if (processing.length === 1) return processing[0] as GeeqLedgerNode
|
|
142
|
+
|
|
143
|
+
const numbers = candidates.map((node) => node.nodeNumber).join(', ')
|
|
144
|
+
throw nodeUnusable(
|
|
145
|
+
`${host} matches ${candidates.length} net actor records (node numbers ${numbers}) ` +
|
|
146
|
+
`and ${processing.length} of them are processing, so the SDK cannot choose. ` +
|
|
147
|
+
'Pass nodeNumber explicitly.',
|
|
148
|
+
)
|
|
149
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { blake3 } from '@noble/hashes/blake3'
|
|
2
|
+
import { invalidArgument, invalidKey } from '../errors'
|
|
3
|
+
|
|
4
|
+
/** Account ids and public keys are both 32 bytes. */
|
|
5
|
+
export const ACCOUNT_ID_BYTES = 32
|
|
6
|
+
export const PUBLIC_KEY_BYTES = 32
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Any way of naming an account that does not need the engine.
|
|
10
|
+
*
|
|
11
|
+
* Structural on purpose: a {@link GeeqKeypair} already carries `accountId`, so it
|
|
12
|
+
* satisfies this without the crypto layer importing the keypair type and without the
|
|
13
|
+
* caller unwrapping anything.
|
|
14
|
+
*/
|
|
15
|
+
export type AccountRef =
|
|
16
|
+
| Uint8Array
|
|
17
|
+
| { readonly accountId: Uint8Array }
|
|
18
|
+
| { readonly publicKey: Uint8Array }
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Derive the 32-byte account id for a public key.
|
|
22
|
+
*
|
|
23
|
+
* This is a pure hash — it needs no engine and no network, so it works before
|
|
24
|
+
* {@link initGeeq} has resolved.
|
|
25
|
+
*/
|
|
26
|
+
export function accountIdFromPublicKey(publicKey: Uint8Array): Uint8Array {
|
|
27
|
+
if (publicKey.length !== PUBLIC_KEY_BYTES) {
|
|
28
|
+
throw invalidKey(
|
|
29
|
+
`A public key must be ${PUBLIC_KEY_BYTES} bytes, received ${publicKey.length}.`,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
return blake3(publicKey, { dkLen: ACCOUNT_ID_BYTES })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve any {@link AccountRef} to a 32-byte account id.
|
|
37
|
+
*
|
|
38
|
+
* Shared by the address layer and the ledger layer: both need to turn "whatever the
|
|
39
|
+
* caller had to hand" into the one value records are keyed by, and two copies of this
|
|
40
|
+
* would drift. Pure — no engine, no network.
|
|
41
|
+
*
|
|
42
|
+
* `accountId` is checked before `publicKey` so that a keypair, which carries both,
|
|
43
|
+
* takes the cheap path instead of re-hashing its own public key.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export function resolveAccountId(account: AccountRef): Uint8Array {
|
|
48
|
+
if (account instanceof Uint8Array) {
|
|
49
|
+
if (account.length !== ACCOUNT_ID_BYTES) {
|
|
50
|
+
throw invalidKey(
|
|
51
|
+
`An account id must be ${ACCOUNT_ID_BYTES} bytes, received ${account.length}.`,
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
return account
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (account !== null && typeof account === 'object') {
|
|
58
|
+
const { accountId } = account as { accountId?: unknown }
|
|
59
|
+
if (accountId !== undefined) {
|
|
60
|
+
if (!(accountId instanceof Uint8Array)) {
|
|
61
|
+
throw invalidKey('accountId must be a Uint8Array.')
|
|
62
|
+
}
|
|
63
|
+
if (accountId.length !== ACCOUNT_ID_BYTES) {
|
|
64
|
+
throw invalidKey(
|
|
65
|
+
`An account id must be ${ACCOUNT_ID_BYTES} bytes, received ${accountId.length}.`,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
return accountId
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { publicKey } = account as { publicKey?: unknown }
|
|
72
|
+
if (publicKey !== undefined) {
|
|
73
|
+
if (!(publicKey instanceof Uint8Array)) {
|
|
74
|
+
throw invalidKey('publicKey must be a Uint8Array.')
|
|
75
|
+
}
|
|
76
|
+
return accountIdFromPublicKey(publicKey)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
throw invalidArgument(
|
|
81
|
+
`Provide a keypair, a ${ACCOUNT_ID_BYTES}-byte accountId, or a ${PUBLIC_KEY_BYTES}-byte publicKey.`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { toBase64 } from '../encoding/base64'
|
|
2
|
+
import { invalidArgument, invalidKey } from '../errors'
|
|
3
|
+
import { WasmKeypair, wasmVerify } from '../runtime/bindings'
|
|
4
|
+
import { assertReady, initGeeq } from '../runtime/init'
|
|
5
|
+
import { DEFAULT_RECORD_TYPE, toWasmCoinType, toWasmRecordType } from '../address/record-type'
|
|
6
|
+
import { createWalletAddressSync } from '../address/wallet-address'
|
|
7
|
+
import type {
|
|
8
|
+
GeeqKeypair,
|
|
9
|
+
MnemonicOptions,
|
|
10
|
+
WalletAddressOptions,
|
|
11
|
+
} from '../types'
|
|
12
|
+
import { PUBLIC_KEY_BYTES, accountIdFromPublicKey } from './account-id'
|
|
13
|
+
import { normalizeMnemonic } from './mnemonic'
|
|
14
|
+
|
|
15
|
+
const SECRET_KEY_BYTES = 32
|
|
16
|
+
const SIGNATURE_BYTES = 64
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wraps an engine-side keypair. The byte fields are copied out once at
|
|
20
|
+
* construction so the object stays fully readable after {@link dispose}, which
|
|
21
|
+
* only releases the engine handle used for signing.
|
|
22
|
+
*/
|
|
23
|
+
class Keypair implements GeeqKeypair {
|
|
24
|
+
readonly publicKey: Uint8Array
|
|
25
|
+
readonly privateKey: Uint8Array
|
|
26
|
+
readonly accountId: Uint8Array
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The engine-side handle.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately a plain non-enumerable property rather than a `#private` field:
|
|
32
|
+
* private fields are unreachable through a Proxy, so a keypair placed in
|
|
33
|
+
* proxy-based reactive state — Vue's `ref`/`reactive`, MobX, Valtio — would
|
|
34
|
+
* throw `TypeError: Cannot read from private field` on every method call.
|
|
35
|
+
* Non-enumerable keeps it out of spreads, `Object.keys` and devtools noise.
|
|
36
|
+
*/
|
|
37
|
+
private handle: WasmKeypair | null
|
|
38
|
+
|
|
39
|
+
constructor(inner: WasmKeypair) {
|
|
40
|
+
this.handle = inner
|
|
41
|
+
Object.defineProperty(this, 'handle', { enumerable: false })
|
|
42
|
+
this.publicKey = inner.exportPublicKey()
|
|
43
|
+
this.privateKey = inner.exportSecretKey()
|
|
44
|
+
this.accountId = accountIdFromPublicKey(this.publicKey)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getAddress(options: WalletAddressOptions): string {
|
|
48
|
+
return createWalletAddressSync({
|
|
49
|
+
accountId: this.accountId,
|
|
50
|
+
chainNumber: options.chainNumber,
|
|
51
|
+
recordType: options.recordType ?? DEFAULT_RECORD_TYPE,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
sign(message: Uint8Array): Uint8Array {
|
|
56
|
+
if (this.handle === null) {
|
|
57
|
+
throw invalidKey('This keypair has been disposed and can no longer sign.')
|
|
58
|
+
}
|
|
59
|
+
if (!(message instanceof Uint8Array)) {
|
|
60
|
+
throw invalidArgument('sign() expects a Uint8Array.')
|
|
61
|
+
}
|
|
62
|
+
return this.handle.sign(message)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
dispose(): void {
|
|
66
|
+
this.handle?.free()
|
|
67
|
+
this.handle = null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Keeps secret material out of logs, `console.log` and error breadcrumbs. */
|
|
71
|
+
toJSON(): { readonly type: 'GeeqKeypair'; readonly publicKey: string } {
|
|
72
|
+
return { type: 'GeeqKeypair', publicKey: toBase64(this.publicKey) }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
toString(): string {
|
|
76
|
+
return '[GeeqKeypair]'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function assertBytes(value: unknown, expected: number, label: string): Uint8Array {
|
|
81
|
+
if (!(value instanceof Uint8Array)) {
|
|
82
|
+
throw invalidKey(`${label} must be a Uint8Array.`)
|
|
83
|
+
}
|
|
84
|
+
if (value.length !== expected) {
|
|
85
|
+
throw invalidKey(`${label} must be ${expected} bytes, received ${value.length}.`)
|
|
86
|
+
}
|
|
87
|
+
return value
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Create a brand new random keypair. Synchronous; requires a loaded engine. */
|
|
91
|
+
export function generateKeypairSync(): GeeqKeypair {
|
|
92
|
+
assertReady('generateKeypairSync')
|
|
93
|
+
return new Keypair(WasmKeypair.generate())
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Create a brand new random keypair, loading the engine if needed. */
|
|
97
|
+
export async function generateKeypair(): Promise<GeeqKeypair> {
|
|
98
|
+
await initGeeq()
|
|
99
|
+
return generateKeypairSync()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Re-create a keypair from stored public and private key bytes. */
|
|
103
|
+
export function importKeypairSync(publicKey: Uint8Array, privateKey: Uint8Array): GeeqKeypair {
|
|
104
|
+
assertReady('importKeypairSync')
|
|
105
|
+
assertBytes(publicKey, PUBLIC_KEY_BYTES, 'publicKey')
|
|
106
|
+
assertBytes(privateKey, SECRET_KEY_BYTES, 'privateKey')
|
|
107
|
+
|
|
108
|
+
let keypair: Keypair
|
|
109
|
+
try {
|
|
110
|
+
// Deliberately derived from the secret key rather than via the engine's
|
|
111
|
+
// import(), which accepts a mismatched pair and stores the given public key
|
|
112
|
+
// verbatim — yielding a keypair whose signatures the chain would reject.
|
|
113
|
+
keypair = new Keypair(WasmKeypair.fromSecretKey(privateKey))
|
|
114
|
+
} catch (cause) {
|
|
115
|
+
throw invalidKey('Could not import this keypair. Check the private key is correct.', cause)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!bytesEqual(keypair.publicKey, publicKey)) {
|
|
119
|
+
keypair.dispose()
|
|
120
|
+
throw invalidKey(
|
|
121
|
+
'publicKey does not match privateKey. Check the two halves came from the same keypair.',
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
return keypair
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Re-create a keypair from stored key bytes, loading the engine if needed. */
|
|
128
|
+
export async function importKeypair(
|
|
129
|
+
publicKey: Uint8Array,
|
|
130
|
+
privateKey: Uint8Array,
|
|
131
|
+
): Promise<GeeqKeypair> {
|
|
132
|
+
await initGeeq()
|
|
133
|
+
return importKeypairSync(publicKey, privateKey)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Recover a keypair from its 32-byte secret key alone. */
|
|
137
|
+
export function keypairFromSecretKeySync(privateKey: Uint8Array): GeeqKeypair {
|
|
138
|
+
assertReady('keypairFromSecretKeySync')
|
|
139
|
+
assertBytes(privateKey, SECRET_KEY_BYTES, 'privateKey')
|
|
140
|
+
try {
|
|
141
|
+
return new Keypair(WasmKeypair.fromSecretKey(privateKey))
|
|
142
|
+
} catch (cause) {
|
|
143
|
+
throw invalidKey('Could not derive a keypair from this secret key.', cause)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Recover a keypair from its secret key, loading the engine if needed. */
|
|
148
|
+
export async function keypairFromSecretKey(privateKey: Uint8Array): Promise<GeeqKeypair> {
|
|
149
|
+
await initGeeq()
|
|
150
|
+
return keypairFromSecretKeySync(privateKey)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Derive a keypair from a BIP-39 mnemonic along Geeq's standard derivation path.
|
|
155
|
+
*
|
|
156
|
+
* The same mnemonic and options always produce the same keypair, so this is how a
|
|
157
|
+
* wallet is restored on another device.
|
|
158
|
+
*/
|
|
159
|
+
export function keypairFromMnemonicSync(
|
|
160
|
+
mnemonic: string,
|
|
161
|
+
options: MnemonicOptions = {},
|
|
162
|
+
): GeeqKeypair {
|
|
163
|
+
assertReady('keypairFromMnemonicSync')
|
|
164
|
+
|
|
165
|
+
if (typeof mnemonic !== 'string' || mnemonic.trim().length === 0) {
|
|
166
|
+
throw invalidArgument('mnemonic must be a non-empty BIP-39 phrase.')
|
|
167
|
+
}
|
|
168
|
+
const account = options.account ?? 0
|
|
169
|
+
const addressIndex = options.addressIndex ?? 0
|
|
170
|
+
for (const [label, value] of [['account', account], ['addressIndex', addressIndex]] as const) {
|
|
171
|
+
if (!Number.isInteger(value) || value < 0 || value > 0x7fffffff) {
|
|
172
|
+
throw invalidArgument(`${label} must be an integer between 0 and 2147483647.`)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
return new Keypair(
|
|
178
|
+
WasmKeypair.tryFromMnemonicGeeqPath(
|
|
179
|
+
normalizeMnemonic(mnemonic),
|
|
180
|
+
options.passphrase ?? '',
|
|
181
|
+
toWasmRecordType(options.recordType ?? DEFAULT_RECORD_TYPE),
|
|
182
|
+
toWasmCoinType(options.coinType ?? 'mainnet'),
|
|
183
|
+
account,
|
|
184
|
+
addressIndex,
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
} catch (cause) {
|
|
188
|
+
throw invalidKey(
|
|
189
|
+
'Could not derive a keypair from this mnemonic. Check the words and the passphrase.',
|
|
190
|
+
cause,
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Derive a keypair from a mnemonic, loading the engine if needed. */
|
|
196
|
+
export async function keypairFromMnemonic(
|
|
197
|
+
mnemonic: string,
|
|
198
|
+
options: MnemonicOptions = {},
|
|
199
|
+
): Promise<GeeqKeypair> {
|
|
200
|
+
await initGeeq()
|
|
201
|
+
return keypairFromMnemonicSync(mnemonic, options)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Verify an Ed25519 signature. Requires a loaded engine. */
|
|
205
|
+
export function verifySignature(
|
|
206
|
+
publicKey: Uint8Array,
|
|
207
|
+
signature: Uint8Array,
|
|
208
|
+
message: Uint8Array,
|
|
209
|
+
): boolean {
|
|
210
|
+
assertReady('verifySignature')
|
|
211
|
+
assertBytes(publicKey, PUBLIC_KEY_BYTES, 'publicKey')
|
|
212
|
+
assertBytes(signature, SIGNATURE_BYTES, 'signature')
|
|
213
|
+
return wasmVerify(publicKey, signature, message)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
217
|
+
if (a.length !== b.length) return false
|
|
218
|
+
let diff = 0
|
|
219
|
+
for (let i = 0; i < a.length; i += 1) diff |= (a[i] as number) ^ (b[i] as number)
|
|
220
|
+
return diff === 0
|
|
221
|
+
}
|