{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://psbt-interop-lab.dev/schemas/suite-manifest-0.2.json",
  "title": "PSBT Interop Lab Custom Suite Manifest",
  "type": "object",
  "additionalProperties": false,
  "required": ["schema", "fixtures", "scenarios"],
  "properties": {
    "schema": { "enum": ["psbt-lab.suite/0.1", "psbt-lab.suite/0.2"] },
    "fixtures": {
      "type": "array",
      "maxItems": 32,
      "items": { "$ref": "#/definitions/fixture" }
    },
    "parserFixtures": {
      "type": "array",
      "maxItems": 32,
      "items": { "$ref": "#/definitions/parserFixture" }
    },
    "scenarios": {
      "type": "array",
      "minItems": 1,
      "maxItems": 64,
      "items": { "$ref": "#/definitions/scenario" }
    }
  },
  "definitions": {
    "safeId": {
      "type": "string",
      "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
    },
    "descriptor": {
      "enum": [
        "p2pkh",
        "p2wpkh",
        "p2sh-p2wpkh",
        "p2sh-p2wsh-2-of-3",
        "p2wsh-single-key",
        "p2wsh-2-of-3",
        "p2tr-keypath",
        "p2tr-scriptpath"
      ]
    },
    "fixtureInput": {
      "type": "object",
      "additionalProperties": false,
      "required": ["descriptor", "sequence"],
      "properties": {
        "descriptor": { "$ref": "#/definitions/descriptor" },
        "sequence": { "type": "integer", "minimum": 0, "maximum": 4294967293 }
      }
    },
    "fixedOutput": {
      "type": "object",
      "additionalProperties": false,
      "required": ["descriptor", "amountSats"],
      "properties": {
        "descriptor": { "$ref": "#/definitions/descriptor" },
        "amountSats": { "type": "integer", "minimum": 1, "maximum": 2100000000000000 }
      }
    },
    "remainderOutput": {
      "type": "object",
      "additionalProperties": false,
      "required": ["descriptor", "remainder"],
      "properties": {
        "descriptor": { "$ref": "#/definitions/descriptor" },
        "remainder": { "const": true }
      }
    },
    "fixture": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "inputs", "outputs", "feeSats", "locktime", "transactionVersion"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "inputs": {
          "type": "array",
          "minItems": 1,
          "maxItems": 8,
          "items": { "$ref": "#/definitions/fixtureInput" }
        },
        "outputs": {
          "type": "array",
          "minItems": 1,
          "maxItems": 8,
          "items": {
            "oneOf": [
              { "$ref": "#/definitions/fixedOutput" },
              { "$ref": "#/definitions/remainderOutput" }
            ]
          }
        },
        "feeSats": { "type": "integer", "minimum": 1, "maximum": 100000000 },
        "locktime": { "type": "integer", "minimum": 0, "maximum": 4294967295 },
        "transactionVersion": { "const": 2 }
      }
    },
    "parserFixture": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "psbt", "sha256"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "psbt": { "type": "string", "minLength": 8, "maxLength": 5592408 },
        "sha256": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
      }
    },
    "adapterStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "adapter", "operation", "input"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "adapter": { "$ref": "#/definitions/safeId" },
        "operation": { "enum": ["roundtrip", "sign", "finalize-inputs"] },
        "input": { "$ref": "#/definitions/safeId" }
      }
    },
    "combineStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "adapter", "operation", "inputs"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "adapter": { "$ref": "#/definitions/safeId" },
        "operation": { "const": "combine" },
        "inputs": {
          "type": "array",
          "minItems": 2,
          "maxItems": 8,
          "items": { "$ref": "#/definitions/safeId" }
        }
      }
    },
    "coreStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "operation", "input"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "operation": { "enum": ["core-finalize", "core-policy-check"] },
        "input": { "$ref": "#/definitions/safeId" }
      }
    },
    "mapLocation": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind"],
          "properties": { "kind": { "const": "global" } }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "index"],
          "properties": {
            "kind": { "enum": ["input", "output"] },
            "index": { "type": "integer", "minimum": 0, "maximum": 255 }
          }
        }
      ]
    },
    "entryMutationBase": {
      "type": "object",
      "properties": {
        "location": { "$ref": "#/definitions/mapLocation" },
        "keyType": { "type": "integer", "minimum": 0, "maximum": 255 },
        "keyDataHex": {
          "type": "string",
          "pattern": "^(?:[0-9a-fA-F]{2})*$",
          "maxLength": 2048
        }
      }
    },
    "mutationRecipe": {
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "location", "keyType", "valueHex"],
          "properties": {
            "kind": { "enum": ["replace-value", "set-entry"] },
            "location": { "$ref": "#/definitions/mapLocation" },
            "keyType": { "type": "integer", "minimum": 0, "maximum": 255 },
            "keyDataHex": {
              "type": "string",
              "pattern": "^(?:[0-9a-fA-F]{2})*$",
              "maxLength": 2048
            },
            "valueHex": {
              "type": "string",
              "pattern": "^(?:[0-9a-fA-F]{2})*$",
              "maxLength": 131072
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "location", "keyType"],
          "properties": {
            "kind": { "enum": ["delete-entry", "duplicate-entry"] },
            "location": { "$ref": "#/definitions/mapLocation" },
            "keyType": { "type": "integer", "minimum": 0, "maximum": 255 },
            "keyDataHex": {
              "type": "string",
              "pattern": "^(?:[0-9a-fA-F]{2})*$",
              "maxLength": 2048
            }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "offset", "mask"],
          "properties": {
            "kind": { "const": "xor-byte" },
            "offset": { "type": "integer", "minimum": 0, "maximum": 4194303 },
            "mask": { "type": "integer", "minimum": 1, "maximum": 255 }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "byteLength"],
          "properties": {
            "kind": { "const": "truncate" },
            "byteLength": { "type": "integer", "minimum": 5, "maximum": 4194303 }
          }
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["kind", "valueHex"],
          "properties": {
            "kind": { "const": "append-bytes" },
            "valueHex": {
              "type": "string",
              "pattern": "^(?:[0-9a-fA-F]{2})+$",
              "maxLength": 131072
            }
          }
        }
      ]
    },
    "mutateStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "operation", "input", "recipes"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "operation": { "const": "mutate" },
        "input": { "$ref": "#/definitions/safeId" },
        "recipes": {
          "type": "array",
          "minItems": 1,
          "maxItems": 64,
          "items": { "$ref": "#/definitions/mutationRecipe" }
        }
      }
    },
    "compareParsersStep": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "operation", "input", "adapters", "expected"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "operation": { "const": "compare-parsers" },
        "input": { "$ref": "#/definitions/safeId" },
        "adapters": {
          "type": "array",
          "minItems": 1,
          "maxItems": 16,
          "items": { "$ref": "#/definitions/safeId" }
        },
        "expected": {
          "type": "object",
          "additionalProperties": {
            "oneOf": [
              {
                "enum": ["accepted", "rejected", "unsupported", "crashed", "timeout"]
              },
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["classification"],
                "properties": {
                  "classification": {
                    "enum": ["accepted", "rejected", "unsupported", "crashed", "timeout"]
                  },
                  "facts": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": ["psbtVersion", "inputs", "outputs"],
                    "properties": {
                      "psbtVersion": { "enum": [0, 2] },
                      "inputs": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 9007199254740991
                      },
                      "outputs": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 9007199254740991
                      }
                    }
                  }
                }
              }
            ]
          },
          "minProperties": 2,
          "maxProperties": 17
        }
      }
    },
    "scenario": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "title", "fixture", "steps"],
      "properties": {
        "id": { "$ref": "#/definitions/safeId" },
        "title": { "type": "string", "minLength": 1, "maxLength": 256 },
        "fixture": { "$ref": "#/definitions/safeId" },
        "steps": {
          "type": "array",
          "minItems": 1,
          "maxItems": 32,
          "items": {
            "oneOf": [
              { "$ref": "#/definitions/adapterStep" },
              { "$ref": "#/definitions/combineStep" },
              { "$ref": "#/definitions/coreStep" },
              { "$ref": "#/definitions/mutateStep" },
              { "$ref": "#/definitions/compareParsersStep" }
            ]
          }
        }
      }
    }
  }
}
