From 60aec239cc2d57ae21d0069c5bbafb346abc9b66 Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Wed, 13 Nov 2024 10:11:40 -0500 Subject: [PATCH] Publish wallet template schema on libauth.org --- .github/workflows/ci.yml | 2 +- .github/workflows/doc.yml | 2 +- assets/schemas/wallet-template-v0.schema.json | 701 ++++++++++++++++++ package.json | 2 +- 4 files changed, 704 insertions(+), 3 deletions(-) create mode 100644 assets/schemas/wallet-template-v0.schema.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54a8fd2c..03cd20fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [18, 20, latest] + node-version: [18, 20, 22] runner_index: [0, 1, 2, 3] runners_per_version: [4] name: Test Node ${{ matrix.node-version }} (${{ matrix.runner_index }}) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 655654f4..3756fe21 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -31,7 +31,7 @@ jobs: - name: Generate Docs run: yarn doc:html - name: Copy assets - run: yarn doc:logo + run: yarn doc:assets - name: Setup Pages uses: actions/configure-pages@v3 - name: Upload artifact diff --git a/assets/schemas/wallet-template-v0.schema.json b/assets/schemas/wallet-template-v0.schema.json new file mode 100644 index 00000000..8171dc70 --- /dev/null +++ b/assets/schemas/wallet-template-v0.schema.json @@ -0,0 +1,701 @@ +{ + "$ref": "#/definitions/WalletTemplate", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "AuthenticationVirtualMachineIdentifier": { + "description": "Allowable identifiers for authentication virtual machine versions. The `BCH` prefix identifies the Bitcoin Cash network, the `XEC` prefix identifies the eCash network, the `BSV` prefix identifies the Bitcoin SV network, and the `BTC` prefix identifies the Bitcoin Core network. VM versions are named according to the date they were deployed on the indicated network.\n\nFor each network prefix, a `_SPEC` VM version is reserved to indicate that the template requires a custom, not-yet-deployed VM version (e.g. one or more CHIPs). By convention, templates marked for `_SPEC` VMs should indicate their requirements in the template description. After deployment of the `_SPEC` VM, when template compatibility is verified, the template's `supported` array should be updated to indicate compatibility with the live VM version.", + "enum": [ + "BCH_2020_05", + "BCH_2021_05", + "BCH_2022_05", + "BCH_2023_05", + "BCH_2024_05", + "BCH_2025_05", + "BCH_2026_05", + "BCH_2027_05", + "BCH_2028_05", + "BCH_2029_05", + "BCH_2030_05", + "BCH_SPEC", + "BSV_2020_02", + "BSV_SPEC", + "BTC_2017_08", + "BTC_SPEC", + "XEC_2020_05", + "XEC_SPEC" + ], + "type": "string" + }, + "WalletTemplate": { + "additionalProperties": false, + "description": "A `WalletTemplate` specifies a set of locking scripts, unlocking scripts, and other information required to use a certain wallet protocol. Templates fully describe wallet protocols in a way that can be shared between software clients.", + "properties": { + "$schema": { + "description": "The URI that identifies the JSON Schema used by this template. Try: `https://libauth.org/schemas/wallet-template-v0.schema.json` to enable documentation, autocompletion, and validation in JSON documents.", + "type": "string" + }, + "description": { + "description": "An optionally multi-line, free-form, human-readable description for this wallet template (for use in user interfaces). If displayed, this description should use a monospace font to properly render ASCII diagrams.\n\nDescriptions have no length limit, but in user interfaces with limited space, they should be hidden beyond the first newline character or `140` characters until revealed by the user (e.g. by hiding the remaining description until the user activates a \"show more\" link).", + "type": "string" + }, + "entities": { + "additionalProperties": { + "$ref": "#/definitions/WalletTemplateEntity" + }, + "description": "A map of entities defined in this wallet template.\n\nObject keys are used as entity identifiers, and by convention, should use `snake_case`.", + "type": "object" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this authentication template (for use in user interfaces).", + "type": "string" + }, + "scenarios": { + "additionalProperties": { + "$ref": "#/definitions/WalletTemplateScenario" + }, + "description": "A scenario describes a context in which one or more scripts might be used. Scenarios are used for transaction estimation and as an integrated testing system for wallet templates.\n\nObject keys are used as scenario identifiers, and by convention, should use `snake_case`.", + "type": "object" + }, + "scripts": { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/definitions/WalletTemplateScript" + }, + { + "$ref": "#/definitions/WalletTemplateScriptLocking" + }, + { + "$ref": "#/definitions/WalletTemplateScriptTested" + }, + { + "$ref": "#/definitions/WalletTemplateScriptUnlocking" + } + ] + }, + "description": "A map of scripts used in this wallet template.\n\nObject keys are used as script identifiers, and by convention, should use `snake_case`.", + "type": "object" + }, + "supported": { + "description": "A list of authentication virtual machine versions supported by this template.\n\nVirtual machine identifiers use the format `CODE_YYYY_MM`, where `CODE` is the currency code used to identify the network, and `YYYY_MM` is the year and month in which the specified VM version became active on the indicated network.\n\nIdentifiers with the `_SPEC` suffix indicate that this template is intended for compatibility with a future virtual machine version, but at the time the template was created, that virtual machine had not yet become active on the specified chain.", + "items": { + "$ref": "#/definitions/AuthenticationVirtualMachineIdentifier" + }, + "type": "array" + }, + "version": { + "const": 0, + "deprecated": "template versions are now specified via `$schema`", + "description": "A number identifying the format of this WalletTemplate. Currently, this implementation allows `version` to be set to `0`.", + "type": "number" + } + }, + "required": ["entities", "scripts", "supported"], + "type": "object" + }, + "WalletTemplateAddressData": { + "additionalProperties": false, + "properties": { + "description": { + "description": "A single-line, human readable description for this variable (for use in user interfaces).", + "type": "string" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this variable (for use in user interfaces).", + "type": "string" + }, + "type": { + "const": "AddressData", + "type": "string" + } + }, + "required": ["type"], + "type": "object" + }, + "WalletTemplateEntity": { + "additionalProperties": false, + "description": "An object describing the configuration for a particular entity within an wallet template.", + "properties": { + "description": { + "description": "An optionally multi-line, free-form, human-readable description for this entity (for use in user interfaces). If displayed, this description should use a monospace font to properly render ASCII diagrams.", + "type": "string" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this entity for use in user interfaces and error messages, e.g.: `Trusted Third-Party`.", + "type": "string" + }, + "scripts": { + "description": "An array of the identifiers of each script the entity must be capable of generating, e.g. each of the unlocking scripts this entity might use.\n\nProvided the necessary variables, any entity can construct any script, but this option allows us to hint to more advanced wallets which scripts to recommend to users. (Especially when many scripts require inter-entity communication initiated by a user.)\n\nIf not provided, this property is assumed to include all scripts in the template.", + "items": { + "type": "string" + }, + "type": "array" + }, + "variables": { + "additionalProperties": { + "$ref": "#/definitions/WalletTemplateVariable" + }, + "description": "A map of variables that must be provided by this entity for use in the template's scripts. Some variables are required before locking script generation, while some variables can or must be resolved only before unlocking script generation.\n\nObject keys are used as variable identifiers, and by convention, should use `snake_case`.", + "type": "object" + } + }, + "type": "object" + }, + "WalletTemplateHdKey": { + "additionalProperties": false, + "properties": { + "addressOffset": { + "description": "The offset by which to increment the `addressIndex` provided in the compilation data when deriving this `HdKey`. (Default: 0)\n\nThis is useful for deriving the \"next\" (`1`) or \"previous\" (`-1`) address for use in the current compilation.", + "type": "number" + }, + "description": { + "description": "A single-line, human readable description for this variable (for use in user interfaces).", + "type": "string" + }, + "hdPublicKeyDerivationPath": { + "description": "The path to derive the entity's HD public key from the entity's provided HD private key. By default, an empty string (`\"\"`), i.e. the entity's HD public key represents the same node in the HD tree as the provided HD private key.\n\nThis can be used to specify another relative or absolute derivation path from which the `publicDerivationPath` begins, e.g. `m/0'/1'/2'`. See `publicDerivationPath` for details.\n\nThis path may optionally begin with an `m` (for relative private derivation) and must be fixed – it cannot contain any `i` characters to represent the address index, as a dynamic hardened path would require a new HD public key for each address.\n\nNote, `hdPublicKeyDerivationPath` could be automatically determined in some cases, but it's always defined independently to improve validation and auditability.", + "type": "string" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this variable (for use in user interfaces).", + "type": "string" + }, + "neverSignTwice": { + "description": "If set to `true`, indicates that this key should never be used to sign two different messages.\n\nThis is useful for contracts that use zero-confirmation escrow systems to guarantee against double-spend attempts. By indicating that the user could be subjected to losses if a key were used in multiple signatures, templates can ensure that wallet implementations apply appropriate safeguards around use of the key.\n\nDefaults to `false`.", + "type": "boolean" + }, + "privateDerivationPath": { + "description": "The relative or absolute derivation path used to derive this `HdKey` from the owning entity's HD private key. By default, `i`.\n\nIf the first character is `m`, the path is an absolute path, otherwise, the path is a relative path. For absolute paths, the compiler will verify that the relevant entity's HD private key is a master private key (encoded with a depth of zero); `HdKey`s with relative `privateDerivationPath` may be resolved using non-master private keys (e.g. hardened accounts that have been previously derived and delegated to a sub-entity prior to compilation).\n\nThis path uses the notation specified in BIP32 and the `i` character to represent the `addressIndex`:\n\nAn optional `m` character (indicating an absolute, private derivation path), followed by sets of `/` and a number representing the child index used in the derivation at that depth. Hardened derivation is represented by a trailing `'`, and hardened child indexes are represented with the hardened index offset (`2147483648`) subtracted. All `i` characters are replaced with the value of `addressIndex` plus this `HdKey`'s `addressOffset`. If the `i` character is followed by `'`, the hardened index offset is added (`2147483648`) and hardened derivation is used.\n\nFor example, `m/0/1'/i'` has 3 levels of derivation, with child indexes in the following order:\n\n`derive(derive(derive(masterKey, 0), 2147483648 + 1), 2147483648 + addressIndex + addressOffset)`\n\nAs the path is absolute (begins with `m`), the compiler will also verify that a zero-depth (\"master\") HD private key is provided for the entity owning this `HdKey`.\n\nNote, because hardened derivation requires knowledge of the private key, `HdKey` variables with `privateDerivationPath`s that include hardened derivation must configure `hdPublicKeyDerivationPath` to support HD public derivation.", + "type": "string" + }, + "publicDerivationPath": { + "description": "The relative derivation path used to derive this `HdKey`'s public key from the owning entity's HD public key (configured via `hdPublicKeyDerivationPath`). If not set, the relative path (following the `m/` of `privateDerivationPath`) is used. For the `privateDerivationPath` default of `i`, this is `i`.\n\nIf `privateDerivationPath` uses hardened derivation for some levels, but later derivation levels use non-hardened derivation, `publicDerivationPath` can be used to specify a public derivation path beginning from `hdPublicKeyDerivationPath` (i.e. `publicDerivationPath` should always be a non-hardened segment of `privateDerivationPath` that follows `hdPublicKeyDerivationPath`).\n\nThe `publicDerivationPath` must be a relative HD derivation path: non-hardened positive integer child indexes (between `0` and `2147483647`, without any trailing `'`s) separated by `/`s.\n\nFor example, if `privateDerivationPath` is `m/0'/i`, it is not possible to derive the equivalent public key with only the HD public key `M`. (The path `M/0'/i` is impossible.) However, given the HD public key for `m/0'`, it is possible to derive the public key of `m/0'/i` for any `i`. In this case, `hdPublicKeyDerivationPath` would be `m/0'` and `publicDerivationPath` would be the remaining `i`.", + "type": "string" + }, + "type": { + "const": "HdKey", + "type": "string" + } + }, + "required": ["type"], + "type": "object" + }, + "WalletTemplateKey": { + "additionalProperties": false, + "properties": { + "description": { + "description": "A single-line, human readable description for this variable (for use in user interfaces).", + "type": "string" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this variable (for use in user interfaces).", + "type": "string" + }, + "neverSignTwice": { + "description": "If set to `true`, indicates that this key should never be used to sign two different messages.\n\nThis is useful for contracts that use zero-confirmation escrow systems to guarantee against double-spend attempts. By indicating that the user could be subjected to losses if a key were used in multiple signatures, templates can ensure that wallet implementations apply appropriate safeguards around use of the key.\n\nDefaults to `false`.", + "type": "boolean" + }, + "type": { + "const": "Key", + "type": "string" + } + }, + "required": ["type"], + "type": "object" + }, + "WalletTemplateScenario": { + "additionalProperties": false, + "description": "An object describing the configuration for a particular scenario within an wallet template.", + "properties": { + "data": { + "$ref": "#/definitions/WalletTemplateScenarioData", + "description": "An object defining the data to use while compiling this scenario. The properties specified here are used to extend the existing scenario data based on this scenario's `extends` property.\n\nEach property is extended individually – to unset a previously-set property, the property must be individually overridden in this object." + }, + "description": { + "description": "An optionally multi-line, free-form, human-readable description for this scenario (for use in user interfaces). If displayed, this description should use a monospace font to properly render ASCII diagrams.", + "type": "string" + }, + "extends": { + "description": "The identifier of the scenario that this scenario extends. Any `data` or `transaction` properties not defined in this scenario inherit from the extended parent scenario.\n\nIf undefined, this scenario is assumed to extend the default scenario:\n\n- The default values for `data` are set: - The identifiers of all `Key` variables and entities in this template are lexicographically sorted, then each is assigned an incrementing positive integer – beginning with `1` – encoded as an unsigned, 256-bit, big-endian integer (i.e. `0x0000...0001` (32 bytes), `0x0000...0002`, `0x0000...0003`, etc.). For `Key`s, this assigned value is used as the private key; For entities, the assigned value is used as the master seed of that entity's `HdPrivateKey`. If `hdKey` is set, the `addressIndex` is set to `0`. - `currentBlockHeight` is set to `2`. This is the height of the second mined block after the genesis block: `000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd`. This default value was chosen to be low enough to simplify the debugging of block height offsets while remaining differentiated from `0` and `1`, which are used both as boolean return values and for control flow. - `currentBlockTime` is set to `1231469665`. This is the Median Time-Past block time (BIP113) of block `2`.\n\n- Then `transaction` is set based on use: - if the scenario is being used for transaction estimation, all transaction properties are taken from the transaction being estimated. - if the scenario is being used for script testing and validation, the default value for each `transaction` property is used.\n\nWhen a scenario is extended, each property of `data` and `transaction` is extended individually: if the extending scenario does not provide a new value for `data.bytecode.value` or `transaction.property`, the parent value is used. To avoid inheriting a parent value, each child value must be individually overridden.", + "type": "string" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this scenario for use in user interfaces, e.g.: `Delayed Recovery`.", + "type": "string" + }, + "sourceOutputs": { + "description": "The list of source outputs (a.k.a. UTXOs) to use when generating the compilation context for this scenario.\n\nThe `sourceOutputs` property must have the same length as `transaction.inputs`, and each source output must be ordered to match the index of the input that spends it.\n\nTo be valid the `sourceOutputs` property must have exactly one source output with `lockingBytecode` set to `[\"slot\"]` – the output at the same index as the `[\"slot\"]` input in `transaction.inputs`.\n\nIf undefined, defaults to `[{ \"lockingBytecode\": [\"slot\"] }]`.", + "items": { + "$ref": "#/definitions/WalletTemplateScenarioSourceOutput" + }, + "type": "array" + }, + "transaction": { + "additionalProperties": false, + "description": "The transaction within which this scenario should be evaluated. This is used for script testing and validation.\n\nIf undefined, inherits the default value for each property: ```json { \"inputs\": [{ \"unlockingBytecode\": ['slot'] }], \"locktime\": 0, \"outputs\": [{ \"lockingBytecode\": {} }], \"version\": 2 } ```\n\nAny `transaction` property that is not set will be inherited from the scenario specified by `extends`. when specifying the `inputs` and `outputs` properties, each input and output extends the default values for inputs and outputs, respectively.\n\nFor example, an input of `{}` is interpreted as: ```json { \"outpointIndex\": 0, \"outpointTransactionHash\": \"0000000000000000000000000000000000000000000000000000000000000000\", \"sequenceNumber\": 0, \"unlockingBytecode\": ['slot'] } ``` And an output of `{}` is interpreted as: ```json { \"lockingBytecode\": { \"script\": ['copy'], \"overrides\": { \"hdKeys\": { \"addressIndex\": 1 } } }, \"valueSatoshis\": 0 } ```", + "properties": { + "inputs": { + "description": "The list of inputs to use when generating the transaction for this scenario.\n\nTo be valid the `inputs` property must have exactly one input with `unlockingBytecode` set to `[\"slot\"]`. This is the input in which the unlocking script under test will be placed.\n\nIf undefined, inherits the default scenario `inputs` value: `[{ \"unlockingBytecode\": [\"slot\"] }]`.", + "items": { + "$ref": "#/definitions/WalletTemplateScenarioInput" + }, + "type": "array" + }, + "locktime": { + "description": "The locktime to use when generating the transaction for this scenario. A positive integer from `0` to a maximum of `4294967295` – if undefined, defaults to `0`.\n\nLocktime can be provided as either a timestamp or a block height. Values less than `500000000` are understood to be a block height (the current block number in the chain, beginning from block `0`). Values greater than or equal to `500000000` are understood to be a UNIX timestamp.\n\nFor validating timestamp values, the median timestamp of the last 11 blocks (Median Time-Past) is used. The precise behavior is defined in BIP113.\n\nIf the `sequenceNumber` of every transaction input is set to `0xffffffff` (`4294967295`), locktime is disabled, and the transaction may be added to a block even if the specified locktime has not yet been reached. When locktime is disabled, if an `OP_CHECKLOCKTIMEVERIFY` operation is encountered during the verification of any input, an error is produced, and the transaction is invalid.", + "type": "number" + }, + "outputs": { + "description": "The list of outputs to use when generating the transaction for this scenario.\n\nIf undefined, defaults to `[{ \"lockingBytecode\": {} }]`.", + "items": { + "$ref": "#/definitions/WalletTemplateScenarioTransactionOutput" + }, + "type": "array" + }, + "version": { + "description": "The version to use when generating the transaction for this scenario. A positive integer from `0` to a maximum of `4294967295` – if undefined, inherits the default scenario `version` value: `2`.", + "type": "number" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "WalletTemplateScenarioBytecode": { + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "overrides": { + "$ref": "#/definitions/WalletTemplateScenarioData", + "description": "Scenario data that extends the scenario's top-level `data` during script compilation.\n\nEach property is extended individually – to modify a property set by the top-level scenario `data`, the new value must be listed here.\n\nDefaults to `{}` for `sourceOutputs` and `transaction.inputs`; defaults to `{ \"hdKeys\": { \"addressIndex\": 1 } }` for `transaction.outputs`." + }, + "script": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "const": "copy", + "type": "string" + }, + "maxItems": 1, + "minItems": 1, + "type": "array" + } + ], + "description": "The identifier of the script to compile when generating this bytecode. May also be set to `[\"copy\"]`, which is automatically replaced with the identifier of the locking or unlocking script under test, respectively.\n\nIf undefined, defaults to `[\"copy\"]`." + } + }, + "type": "object" + } + ], + "description": "A type that describes the configuration for a particular locking or unlocking bytecode within a wallet template scenario.\n\nBytecode may be specified as either a hexadecimal-encoded string or an object describing the required compilation.\n\nFor `sourceOutputs` and `transaction.inputs`, defaults to `{ script: [\"copy\"], overrides: {} }`. For `transaction.outputs`, defaults to `{ script: [\"copy\"], overrides: { \"hdKeys\": { \"addressIndex\": 1 } } }`." + }, + "WalletTemplateScenarioData": { + "additionalProperties": false, + "description": "An object defining the data to use while compiling a scenario.", + "properties": { + "bytecode": { + "additionalProperties": { + "type": "string" + }, + "description": "A map of full identifiers to CashAssembly scripts that compile to each identifier's value for this scenario. Allowing `bytecode` to be specified as scripts (rather than e.g. hex) offers greater power and flexibility.\n\nBytecode scripts have access to each other and all other template scripts and defined variables, however, cyclical references will produce an error at compile time. Also, because the results of these compilations will be used to generate the compilation context for this scenario, these scripts may not use compiler operations that themselves require access to compilation context (e.g. signatures).\n\nThe provided `fullIdentifier` should match the complete identifier for each item, e.g. `some_wallet_data`, `variable_id.public_key`, or `variable_id.schnorr_signature.all_outputs`.\n\nAll `AddressData` and `WalletData` variables must be provided via `bytecode` (though the default scenario automatically includes reasonable values), and pre-computed results for operations of other variable types (e.g. `key.public_key`) may also be provided via this property.\n\nBecause each bytecode identifier may precisely match the identifier of the variable it defines for this scenario, references between these scripts must refer to the target script with a `_scenario.` prefix. E.g. to reference a sibling script `my_foo` from `my_bar`, the `my_bar` script must use the identifier `_scenario.my_foo`.", + "type": "object" + }, + "currentBlockHeight": { + "description": "The current block height at the \"address creation time\" implied in this scenario.", + "type": "number" + }, + "currentBlockTime": { + "description": "The current MTP block time as a UNIX timestamp at the \"address creation time\" implied in this scenario.\n\nNote, this is never a current timestamp, but rather the median timestamp of the last 11 blocks. It is therefore approximately one hour in the past.\n\nEvery block has a precise MTP block time, much like a block height. See BIP113 for details.", + "type": "number" + }, + "hdKeys": { + "additionalProperties": false, + "description": "An object describing the settings used for `HdKey` variables in this scenario.", + "properties": { + "addressIndex": { + "description": "The current address index to be used for this scenario. The `addressIndex` gets added to each `HdKey`s `addressOffset` to calculate the dynamic index (`i`) used in each `privateDerivationPath` or `publicDerivationPath`.\n\nThis is required for any compiler operation that requires derivation. Typically, the value is incremented by one for each address in a wallet.\n\nDefaults to `0`.", + "type": "number" + }, + "hdPrivateKeys": { + "additionalProperties": { + "type": "string" + }, + "description": "A map of entity IDs to master HD private keys. These master HD private keys are used to derive each `HdKey` variable assigned to that entity according to its `privateDerivationPath`.\n\nHD private keys may be encoded for either mainnet or testnet (the network information is ignored).\n\nIf both an HD private key (in `hdPrivateKeys`) and HD public key (in `hdPublicKeys`) are provided for the same entity in the same scenario (not recommended), the HD private key is used.", + "type": "object" + }, + "hdPublicKeys": { + "additionalProperties": { + "type": "string" + }, + "description": "A map of entity IDs to HD public keys. These HD public keys are used to derive public keys for each `HdKey` variable assigned to that entity according to its `publicDerivationPath`.\n\nHD public keys may be encoded for either mainnet or testnet (the network information is ignored).\n\nIf both an HD private key (in `hdPrivateKeys`) and HD public key (in `hdPublicKeys`) are provided for the same entity in the same scenario (not recommended), the HD private key is used.", + "type": "object" + } + }, + "type": "object" + }, + "keys": { + "additionalProperties": false, + "description": "An object describing the settings used for `Key` variables in this scenario.", + "properties": { + "privateKeys": { + "additionalProperties": { + "type": "string" + }, + "description": "A map of `Key` variable IDs to their 32-byte, hexadecimal-encoded private key values.", + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "WalletTemplateScenarioInput": { + "additionalProperties": false, + "description": "An example input used to define a scenario for a wallet template.", + "properties": { + "outpointIndex": { + "description": "The index of the output in the transaction from which this input is spent.\n\nIf undefined, this defaults to the same index as the input itself (so that by default, every outpoint in the produced transaction is different, even if an empty `outpointTransactionHash` is used for each transaction).", + "type": "number" + }, + "outpointTransactionHash": { + "description": "A 32-byte, hexadecimal-encoded hash of the transaction from which this input is spent in big-endian byte order. This is the byte order typically seen in block explorers and user interfaces (as opposed to little-endian byte order, which is used in standard P2P network messages).\n\nIf undefined, this defaults to the value: `0000000000000000000000000000000000000000000000000000000000000001`\n\nA.K.A. Outpoint `Transaction ID`", + "type": "string" + }, + "sequenceNumber": { + "description": "The positive, 32-bit unsigned integer used as the \"sequence number\" for this input.\n\nIf undefined, this defaults to `0`.", + "type": "number" + }, + "unlockingBytecode": { + "anyOf": [ + { + "$ref": "#/definitions/WalletTemplateScenarioBytecode" + }, + { + "items": { + "const": "slot", + "type": "string" + }, + "maxItems": 1, + "minItems": 1, + "type": "array" + } + ], + "description": "The `unlockingBytecode` value of this input for this scenario. This must be either `[\"slot\"]`, indicating that this input contains the `unlockingBytecode` under test by the scenario, or an `WalletTemplateScenarioBytecode`.\n\nFor a scenario to be valid, `unlockingBytecode` must be `[\"slot\"]` for exactly one input in the scenario.\n\nDefaults to `[\"slot\"]`." + } + }, + "type": "object" + }, + "WalletTemplateScenarioOutput": { + "additionalProperties": false, + "description": "An example output used to define a scenario for a wallet template.", + "properties": { + "lockingBytecode": { + "$ref": "#/definitions/WalletTemplateScenarioBytecode", + "description": "The locking bytecode used to encumber this output.\n\n`lockingBytecode` values may be provided as a hexadecimal-encoded string or as an object describing the required compilation. If undefined, defaults to `{}`, which uses the default values for `script` and `overrides`, respectively.\n\nOnly source outputs may specify a `lockingBytecode` of `[\"slot\"]`; this identifies the source output in which the locking script under test will be placed. (To be valid, every scenario's `sourceOutputs` property must have exactly one source output slot and one input slot at the same index.)" + }, + "token": { + "additionalProperties": false, + "description": "The CashToken contents of this output. This property is only defined if the output contains one or more tokens. For details, see `CHIP-2022-02-CashTokens`.", + "properties": { + "amount": { + "description": "The number of fungible tokens (of `category`) held in this output.\n\nBecause `Number.MAX_SAFE_INTEGER` (`9007199254740991`) is less than the maximum token amount (`9223372036854775807`), this value may also be provided as a string, e.g. `\"9223372036854775807\"`.\n\nIf undefined, this defaults to: `0`.", + "type": ["number", "string"] + }, + "category": { + "description": "The 32-byte, hexadecimal-encoded token category ID to which the token(s) in this output belong in big-endian byte order. This is the byte order typically seen in block explorers and user interfaces (as opposed to little-endian byte order, which is used in standard P2P network messages).\n\nIf undefined, this defaults to the value: `0000000000000000000000000000000000000000000000000000000000000002`", + "type": "string" + }, + "nft": { + "additionalProperties": false, + "description": "If present, the non-fungible token (NFT) held by this output. If the output does not include a non-fungible token, `undefined`.", + "properties": { + "capability": { + "description": "The capability of this non-fungible token, must be either `minting`, `mutable`, or `none`.\n\nIf undefined, this defaults to: `none`.", + "enum": ["minting", "mutable", "none"], + "type": "string" + }, + "commitment": { + "description": "The hex-encoded commitment contents included in the non-fungible token held in this output.\n\nIf undefined, this defaults to: `\"\"` (a zero-length commitment).", + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "valueSatoshis": { + "description": "The value of the output in satoshis, the smallest unit of bitcoin.\n\nIn a valid transaction, this is a positive integer, from `0` to the maximum number of satoshis available to the transaction.\n\nThe maximum number of satoshis in existence is about 1/4 of `Number.MAX_SAFE_INTEGER` (`9007199254740991`), so typically, this value is defined using a `number`. However, this value may also be defined using a 16-character, hexadecimal-encoded `string`, to allow for the full range of the 64-bit unsigned, little-endian integer used to encode `valueSatoshis` in the encoded output format, e.g. `\"ffffffffffffffff\"`. This is useful for representing scenarios where intentionally excessive values are provided (to ensure an otherwise properly-signed transaction can never be included in the blockchain), e.g. transaction size estimations or off-chain Bitauth signatures.\n\nIf undefined, this defaults to: `0`.", + "type": ["number", "string"] + } + }, + "type": "object" + }, + "WalletTemplateScenarioOutput": { + "additionalProperties": false, + "description": "An example output used to define a scenario for a wallet template.", + "properties": { + "lockingBytecode": { + "anyOf": [ + { + "$ref": "#/definitions/WalletTemplateScenarioBytecode" + }, + { + "items": { + "const": "slot", + "type": "string" + }, + "maxItems": 1, + "minItems": 1, + "type": "array" + } + ], + "description": "The locking bytecode used to encumber this output.\n\n`lockingBytecode` values may be provided as a hexadecimal-encoded string or as an object describing the required compilation. If undefined, defaults to `{}`, which uses the default values for `script` and `overrides`, respectively.\n\nOnly source outputs may specify a `lockingBytecode` of `[\"slot\"]`; this identifies the source output in which the locking script under test will be placed. (To be valid, every scenario's `sourceOutputs` property must have exactly one source output slot and one input slot at the same index.)" + }, + "token": { + "additionalProperties": false, + "description": "The CashToken contents of this output. This property is only defined if the output contains one or more tokens. For details, see `CHIP-2022-02-CashTokens`.", + "properties": { + "amount": { + "description": "The number of fungible tokens (of `category`) held in this output.\n\nBecause `Number.MAX_SAFE_INTEGER` (`9007199254740991`) is less than the maximum token amount (`9223372036854775807`), this value may also be provided as a string, e.g. `\"9223372036854775807\"`.\n\nIf undefined, this defaults to: `0`.", + "type": ["number", "string"] + }, + "category": { + "description": "The 32-byte, hexadecimal-encoded token category ID to which the token(s) in this output belong in big-endian byte order. This is the byte order typically seen in block explorers and user interfaces (as opposed to little-endian byte order, which is used in standard P2P network messages).\n\nIf undefined, this defaults to the value: `0000000000000000000000000000000000000000000000000000000000000002`", + "type": "string" + }, + "nft": { + "additionalProperties": false, + "description": "If present, the non-fungible token (NFT) held by this output. If the output does not include a non-fungible token, `undefined`.", + "properties": { + "capability": { + "description": "The capability of this non-fungible token, must be either `minting`, `mutable`, or `none`.\n\nIf undefined, this defaults to: `none`.", + "enum": ["minting", "mutable", "none"], + "type": "string" + }, + "commitment": { + "description": "The hex-encoded commitment contents included in the non-fungible token held in this output.\n\nIf undefined, this defaults to: `\"\"` (a zero-length commitment).", + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "valueSatoshis": { + "description": "The value of the output in satoshis, the smallest unit of bitcoin.\n\nIn a valid transaction, this is a positive integer, from `0` to the maximum number of satoshis available to the transaction.\n\nThe maximum number of satoshis in existence is about 1/4 of `Number.MAX_SAFE_INTEGER` (`9007199254740991`), so typically, this value is defined using a `number`. However, this value may also be defined using a 16-character, hexadecimal-encoded `string`, to allow for the full range of the 64-bit unsigned, little-endian integer used to encode `valueSatoshis` in the encoded output format, e.g. `\"ffffffffffffffff\"`. This is useful for representing scenarios where intentionally excessive values are provided (to ensure an otherwise properly-signed transaction can never be included in the blockchain), e.g. transaction size estimations or off-chain Bitauth signatures.\n\nIf undefined, this defaults to: `0`.", + "type": ["number", "string"] + } + }, + "type": "object" + }, + "WalletTemplateScenarioSourceOutput": { + "$ref": "#/definitions/WalletTemplateScenarioOutput", + "description": "A source output used by a wallet template scenario." + }, + "WalletTemplateScenarioTransactionOutput": { + "$ref": "#/definitions/WalletTemplateScenarioOutput", + "description": "A transaction output used to define a wallet template scenario transaction." + }, + "WalletTemplateScript": { + "additionalProperties": false, + "description": "An object describing the configuration for a particular script within an wallet template.", + "properties": { + "name": { + "description": "A single-line, human-readable name for this script (for use in user interfaces).", + "type": "string" + }, + "script": { + "description": "The script definition in CashAssembly.", + "type": "string" + } + }, + "required": ["script"], + "type": "object" + }, + "WalletTemplateScriptLocking": { + "additionalProperties": false, + "properties": { + "lockingType": { + "description": "Indicates if P2SH20 infrastructure should be used when producing bytecode related to this script. For more information on P2SH20, see BIP16.\n\nWhen compiling locking scripts of type `p2sh20`, the result will be placed in a P2SH20 \"redeem script\" format: `OP_HASH160 <$( OP_HASH160)> OP_EQUAL`\n\nWhen compiling unlocking scripts that unlock locking scripts of type `p2sh20`, the result will be transformed into the P2SH20 unlocking format: `unlockingBytecode ` (where `lockingBytecode` is the compiled bytecode of the locking script, without the \"redeem script\" transformation.)\n\nThe presence of the `lockingType` property indicates that this script is a locking script. It must be present on any script referenced by the `unlocks` property of another script.", + "enum": ["p2sh20", "p2sh32", "standard"], + "type": "string" + }, + "name": { + "description": "A single-line, human-readable name for this script (for use in user interfaces).", + "type": "string" + }, + "script": { + "description": "The script definition in CashAssembly.", + "type": "string" + } + }, + "required": ["lockingType", "script"], + "type": "object" + }, + "WalletTemplateScriptTest": { + "additionalProperties": false, + "properties": { + "check": { + "description": "The script to evaluate after the script being tested. This can be used to check that the tested script leaves the expected results on the stack. For example, if the tested script is expected to leave 3 items of a specific size on the stack, the `check` script could pop each resulting item from the stack and examine it for correctness.\n\nIn scenario testing, this script is appended to the script under test, and together they are treated as the locking script. Program evaluation is considered successful if the resulting program state can be verified by the virtual machine (e.g. the resulting stack contains a single `1`, no errors are produced, etc.).", + "type": "string" + }, + "fails": { + "description": "A list of the scenario identifiers that – when used to compile this test and the script it tests – result in bytecode that fails program verification. The `setup` script is used in place of an unlocking script, and the concatenation of the script under test and the `check` script are used in place of a locking script.\n\nThese scenarios can be used to test this script in development and review.", + "items": { + "type": "string" + }, + "type": "array" + }, + "invalid": { + "description": "A list of the scenario identifiers that – when used to compile this test and the script it tests – result in a compilation error. The `setup` script is used in place of an unlocking script, and the concatenation of the script under test and the `check` script are used in place of a locking script.\n\nThese scenarios can be used to test this script in development and review.", + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this test (for use in user interfaces).", + "type": "string" + }, + "passes": { + "description": "A list of the scenario identifiers that – when used to compile this test and the script it tests – result in bytecode that passes program verification. The `setup` script is used in place of an unlocking script, and the concatenation of the script under test and the `check` script are used in place of a locking script.\n\nThese scenarios can be used to test this script in development and review.", + "items": { + "type": "string" + }, + "type": "array" + }, + "setup": { + "description": "A script to evaluate before the script being tested. This can be used to push values to the stack that are operated on by the tested script.\n\nIn scenario testing, this script is treated as the unlocking script.", + "type": "string" + } + }, + "required": ["check"], + "type": "object" + }, + "WalletTemplateScriptTested": { + "additionalProperties": false, + "properties": { + "name": { + "description": "A single-line, human-readable name for this script (for use in user interfaces).", + "type": "string" + }, + "pushed": { + "description": "If set to `true`, indicates that this script should be wrapped in a push statement for testing.\n\nThis is useful for scripts that serve as \"bytecode templates\" – e.g. formatted messages or signature preimages. These scripts are typically not evaluated as bytecode but appear within push statements elsewhere in the template.\n\nDefaults to `false`.", + "type": "boolean" + }, + "script": { + "description": "The script definition in CashAssembly.", + "type": "string" + }, + "tests": { + "additionalProperties": { + "$ref": "#/definitions/WalletTemplateScriptTest" + }, + "description": "One or more tests that can be used during development and during template validation to confirm the correctness of this tested script.", + "type": "object" + } + }, + "required": ["script", "tests"], + "type": "object" + }, + "WalletTemplateScriptUnlocking": { + "additionalProperties": false, + "properties": { + "ageLock": { + "description": "TODO: not yet implemented\n\nThe minimum input age required for this unlocking script to become valid.\n\nThis value is provided as a CashAssembly script that must compile to the least significant 3 bytes of the minimum sequence number required for this unlocking script to be valid (the \"type bit\" and the 2-byte \"value\" – see BIP68 for details). This script has access to all other template scripts and variables, but cyclical references will produce an error at compile time.\n\nIn supporting wallets, this value can be computed at address creation time, and the remaining time for which any UTXO remains \"age-locked\" can be displayed in user interfaces (by parsing the \"type bit\" and \"value\" as described in BIP68).\n\nNote, because the precise value used by `OP_CHECKSEQUENCEVERIFY` can be provided in the unlocking script, it is trivial to create an unlocking script for which a proper value for `ageLock` is not possible to determine until the spending transaction is prepared. These cases are intentionally out-of-scope for this property. Instead, `ageLock` should only be used for unlocking scripts where the expected value can be compiled at address creation time.", + "type": "string" + }, + "estimate": { + "description": "The identifier of the scenario to use for this unlocking script when compiling an estimated transaction.\n\nUsing estimate scenarios, it's possible for wallet software to compute an \"estimated transaction\", an invalid transaction that is guaranteed to be the same byte length as the final transaction. This length can be used to calculate the required transaction fee and assign values to the transaction's change output(s). Because estimate scenarios provide \"estimated\" values for all variables, this estimation can be done by a single entity without input from other entities.\n\nIf not provided, the default scenario will be used for estimation. The default scenario only provides values for each `Key` and `HdKey` variable, so compilations requiring other variables will produce errors. See `WalletTemplateScenario.extends` for details.", + "type": "string" + }, + "fails": { + "description": "A list of the scenario identifiers that – when used to compile this unlocking script and the script it unlocks – result in bytecode that fails program verification.\n\nThese scenarios can be used to test this script in development and review.", + "items": { + "type": "string" + }, + "type": "array" + }, + "invalid": { + "description": "A list of the scenario identifiers that – when used to compile this unlocking script and the script it unlocks – result in a compilation error.\n\nThese scenarios can be used to test this script in development and review.", + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "description": "A single-line, human-readable name for this script (for use in user interfaces).", + "type": "string" + }, + "passes": { + "description": "A list of the scenario identifiers that – when used to compile this unlocking script and the script it unlocks – result in bytecode that passes program verification.\n\nThese scenarios can be used to test this script in development and review.", + "items": { + "type": "string" + }, + "type": "array" + }, + "script": { + "description": "The script definition in CashAssembly.", + "type": "string" + }, + "timeLockType": { + "description": "The expected type of time locks in this script.\n\nBecause `OP_CHECKLOCKTIMEVERIFY` reads from a transaction's `locktime` property, every input to a given transaction must share the same time lock type. This differs from `OP_CHECKSEQUENCEVERIFY` in that each input has its own `sequenceNumber`, so compatibility is not required.\n\nIf a transaction includes multiple inputs using scripts with `timeLockType` defined, and the types are not compatible, generation should fail.\n\nThe `timestamp` type indicates that the transaction's locktime is provided as a UNIX timestamp (the `locktime` value is greater than or equal to `500000000`).\n\nThe `height` type indicates that the transaction's locktime is provided as a block height (the `locktime` value is less than `500000000`).\n\nIf `timeLockType` is undefined, the script is assumed to have no reliance on absolute time locks.", + "enum": ["height", "timestamp"], + "type": "string" + }, + "unlocks": { + "description": "The identifier of the script that can be unlocked by this script.\n\nThe presence of the `unlocks` property indicates that this script is an unlocking script, and the script it unlocks must be a locking script.", + "type": "string" + } + }, + "required": ["script", "unlocks"], + "type": "object" + }, + "WalletTemplateVariable": { + "anyOf": [ + { + "$ref": "#/definitions/WalletTemplateAddressData" + }, + { + "$ref": "#/definitions/WalletTemplateHdKey" + }, + { + "$ref": "#/definitions/WalletTemplateKey" + }, + { + "$ref": "#/definitions/WalletTemplateWalletData" + } + ] + }, + "WalletTemplateWalletData": { + "additionalProperties": false, + "properties": { + "description": { + "description": "A single-line, human readable description for this variable (for use in user interfaces).", + "type": "string" + }, + "name": { + "description": "A single-line, Title Case, human-readable name for this variable (for use in user interfaces).", + "type": "string" + }, + "type": { + "const": "WalletData", + "type": "string" + } + }, + "required": ["type"], + "type": "object" + } + } +} diff --git a/package.json b/package.json index d19ea915..3deb228c 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "cov:html": "c8 report --reporter=html", "cov:lcov": "c8 report --reporter=lcov", "doc": "yarn doc:html && yarn doc:html && open-cli build/docs/index.html", - "doc:logo": "cpy assets/libauth.svg build/docs", + "doc:assets": "cp assets/libauth.svg build/docs/assets/libauth.svg && mkdir build/docs/schemas && cp assets/schemas/wallet-template-v0.schema.json build/docs/schemas/wallet-template-v0.schema.json", "doc:html": "typedoc src/index.ts --out build/docs", "doc:next": "yarn build && yarn doc:extract && yarn doc:generate", "doc:extract": "mkdir -p build/api && api-extractor run --local --typescript-compiler-folder node_modules/typescript",