├── .bettercodehub.yml ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── documentation_needed.md │ └── feature_request.md └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── publish.yml │ └── release.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .remarkrc ├── .snyk ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── jest.config.js ├── package.json ├── sonar-project.properties ├── src ├── bound-witness │ ├── index.ts │ ├── spec │ │ ├── xyo-bound-witness.spec.ts │ │ └── xyo-zig-zag-bound-witness.spec.ts │ ├── xyo-bound-witness-handler.ts │ ├── xyo-bound-witness-validator.ts │ ├── xyo-bound-witness.ts │ ├── xyo-zig-zag-bound-witness-handler.ts │ ├── xyo-zig-zag-bound-witness-session.ts │ └── xyo-zig-zag-bound-witness.ts ├── hashing │ ├── index.ts │ ├── xyo-hasher.ts │ └── xyo-sha256.ts ├── heuristics │ ├── common │ │ ├── defaultResolvers.ts │ │ ├── index.ts │ │ ├── resolvers.ts │ │ └── xyo-heuristics-common-util.ts │ ├── index.ts │ ├── spec │ │ └── xyo-heuristic-resolver.spec.ts │ ├── xyo-bound-witness-origin.ts │ ├── xyo-heuristic-getter.ts │ ├── xyo-heuristic-resolver.ts │ └── xyo-payload.ts ├── index.ts ├── network │ ├── index.ts │ ├── tcp │ │ ├── index.ts │ │ ├── xyo-tcp-pipe.ts │ │ └── xyo-tcp-server-network.ts │ ├── xyo-advertise-packet.ts │ ├── xyo-catalog-flags.ts │ ├── xyo-choice-packet.ts │ ├── xyo-network-handler.ts │ ├── xyo-network-pipe.ts │ └── xyo-procedure-catalog.ts ├── object-model │ ├── index.ts │ ├── size-util.ts │ ├── spec │ │ ├── xyo-buffer-test.spec.ts │ │ ├── xyo-iterable-structure.spec.ts │ │ ├── xyo-schema-test.spec.ts │ │ └── xyo-structure.spec.ts │ ├── xyo-buffer.ts │ ├── xyo-iterable-structure.ts │ ├── xyo-iterator.ts │ ├── xyo-schema.ts │ ├── xyo-size.ts │ └── xyo-structure.ts ├── origin │ ├── index.ts │ ├── xyo-bound-witness-inserter.ts │ ├── xyo-genesis-block-creator.ts │ ├── xyo-origin-payload-constructor.ts │ ├── xyo-origin-state.ts │ └── xyo-payload-constructor.ts ├── persist │ ├── index.ts │ ├── spec │ │ └── xyo-origin-state-repository.spec.ts │ ├── xyo-file-origin-state-repository.ts │ ├── xyo-memory-block-repository.ts │ ├── xyo-origin-block-repository │ │ ├── BlockByPublicKeyRepository.ts │ │ ├── BlocksByGeohashRepository.ts │ │ ├── BlocksByTime.ts │ │ ├── OriginBlockGetter.ts │ │ ├── OriginBlockRepository.ts │ │ └── index.ts │ └── xyo-origin-state-repository.ts ├── schema │ └── index.ts ├── signing │ ├── ecdsa │ │ ├── index.ts │ │ └── xyo-secp256k1.ts │ ├── index.ts │ └── xyo-signer.ts ├── simulation │ ├── index.ts │ ├── spec │ │ └── xyo-bound-witness-json.spec.ts │ ├── xyo-ram-origin-state-repository.ts │ └── xyo-stub-signature.ts └── types │ └── bs58.d.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.bettercodehub.yml: -------------------------------------------------------------------------------- 1 | component_depth: 2 2 | languages: 3 | - typescript -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | scripts 4 | coverage 5 | dist/ 6 | __mocks__/ 7 | docs 8 | test/ 9 | tools/ 10 | rollup.config.ts -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "ignorePatterns": ["node_modules/", "build/"], 5 | "plugins": [ 6 | "@typescript-eslint", 7 | "prettier", 8 | "sort-keys-fix", 9 | "typescript-sort-keys", 10 | "json", 11 | "workspaces", 12 | "import", 13 | "simple-import-sort", 14 | "no-secrets" 15 | ], 16 | "extends": [ 17 | "eslint:recommended", 18 | "plugin:import/errors", 19 | "plugin:import/warnings", 20 | "plugin:import/typescript", 21 | "plugin:@typescript-eslint/recommended", 22 | "plugin:json/recommended", 23 | "plugin:prettier/recommended", 24 | "plugin:workspaces/recommended" 25 | ], 26 | "settings": { 27 | "import/parsers": { 28 | "@typescript-eslint/parser": [".ts", ".tsx"] 29 | } 30 | }, 31 | "rules": { 32 | "no-secrets/no-secrets": ["off"], 33 | "workspaces/require-dependency": ["off"], 34 | "prettier/prettier": "warn", 35 | "semi": ["warn", "never"], 36 | "@typescript-eslint/semi": ["warn", "never"], 37 | 38 | "no-tabs": ["error"], 39 | "@typescript-eslint/member-delimiter-style": [ 40 | "error", 41 | { 42 | "multiline": { 43 | "delimiter": "none", 44 | "requireLast": true 45 | }, 46 | "singleline": { 47 | "delimiter": "semi", 48 | "requireLast": false 49 | } 50 | } 51 | ], 52 | "require-await": "error", 53 | "sort-keys": ["warn", "asc", { "caseSensitive": true, "natural": false, "minKeys": 2 }], 54 | "typescript-sort-keys/interface": "warn", 55 | "typescript-sort-keys/string-enum": "warn", 56 | "sort-keys-fix/sort-keys-fix": "warn", 57 | "no-unused-vars": "off", 58 | "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], 59 | "@typescript-eslint/no-explicit-any": "off", 60 | "@typescript-eslint/explicit-module-boundary-types": "off", 61 | "quotes": [2, "single", "avoid-escape"], 62 | "max-statements": ["error", 32], 63 | "max-depth": ["error", 6], 64 | "complexity": ["error", 20], 65 | "max-lines": ["error", { "max": 512, "skipBlankLines": true }], 66 | "max-nested-callbacks": ["error", 6], 67 | "import/no-named-as-default": ["off"], 68 | "import/no-self-import": ["warn"], 69 | "import/no-absolute-path": ["warn"], 70 | "import/no-internal-modules": [ "off", { 71 | "allow": [ 72 | "react-icons/*", 73 | "*/img/**", 74 | "source-map-support/*", 75 | "lodash/*", 76 | "@material-ui/**", 77 | "aws-sdk/**", 78 | "firebase/**", 79 | "@ethersproject/**", 80 | "aos/**", 81 | "filepond*/**", 82 | "pure-react-carousel/**", 83 | "mapbox-gl/**", 84 | "react-share/**", 85 | "sdk-xyoworld-typechain/**", 86 | "react-git-info/**", 87 | "react-player/**" 88 | ] 89 | } ], 90 | "import/no-restricted-paths": ["warn"], 91 | "import/namespace": ["warn"], 92 | "import/no-cycle": ["warn"], 93 | "import/no-useless-path-segments": ["warn"], 94 | "simple-import-sort/imports": ["warn"], 95 | "simple-import-sort/exports": ["warn"], 96 | "workspaces/no-relative-imports": ["off"], 97 | "workspaces/no-absolute-imports": ["off"] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve the XYO Android SDK 4 | title: '[BUG]' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Observed behavior** 10 | A clear and concise description of what exactly happened. 11 | 12 | **Expected behavior** 13 | A clear and concise description of what you expected to happen. 14 | 15 | **To Reproduce** 16 | Steps to reproduce the behavior: 17 | 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | 28 | - Device: [e.g. Samsung Galaxy, Google Pixel] 29 | - OS: [e.g. Android 10] 30 | - Browser [e.g. stock browser, chrome] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: XYO Developer Portal 4 | url: https://developers.xyo.network/ 5 | about: XYO Foundation Developer Portal 6 | - name: XYO Foundation Site 7 | url: https://xyo.network/ 8 | about: Check out the fundamentals of our Foundation here -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation_needed.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation needed 3 | about: Suggest documentation that is needed 4 | title: '[DOCUMENTATION]:' 5 | labels: documentation 6 | assignees: '' 7 | --- 8 | 9 | **Is your documentation request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the documentation and format you would like** 13 | A clear and concise description of what documentation you would like to see and what type for format. 14 | Ex. Step-by-step, Paragraph explainer, screenshots, etc. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the document request here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for XYO SDK Android 4 | title: '[FEATURE]' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. This could include specific devices, android versions, etc. -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - 'master' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install 16 | run: yarn install 17 | - name: Build 18 | run: yarn build 19 | - name: Test 20 | run: yarn test -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master, ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 9 * * 4' 11 | 12 | jobs: 13 | analyse: 14 | name: Analyse 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v1 33 | # Override language selection by uncommenting this and choosing your languages 34 | # with: 35 | # languages: go, javascript, csharp, python, cpp, java 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v1 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v1 55 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: '10.x' 16 | - run: | 17 | yarn install 18 | yarn build 19 | 20 | publish-npm: 21 | needs: build 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-node@v1 26 | with: 27 | node-version: '10.x' 28 | registry-url: https://registry.npmjs.org/ 29 | scope: '@xyo-network' 30 | - run: | 31 | yarn install 32 | yarn build 33 | - run: | 34 | echo "//registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN" > /home/runner/work/_temp/.npmrc 35 | echo "_auth=$NODE_AUTH_TOKEN" >> /home/runner/work/_temp/.npmrc 36 | echo "email=$NPM_EMAIL" >> /home/runner/work/_temp/.npmrc 37 | echo "always-auth=true" >> /home/runner/work/_temp/.npmrc 38 | env: 39 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 40 | NPM_EMAIL: ${{ secrets.NPM_EMAIL }} 41 | - run: npm publish --access public -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 10.15.0 16 | - run: yarn install 17 | - run: yarn build 18 | - run: yarn test 19 | 20 | publish-gpr: 21 | needs: build 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-node@v1 26 | with: 27 | node-version: 10.15.0 28 | registry-url: https://npm.pkg.github.com/ 29 | scope: '@xyoraclenetwork' 30 | - run: yarn install 31 | - run: yarn build 32 | - run: yarn publish 33 | env: 34 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | package-lock.json 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | # dist 62 | dist/ 63 | 64 | # OS specific 65 | .DS_Store 66 | 67 | # data 68 | data/ 69 | archivist-db/ 70 | db.json 71 | diviner-db/ 72 | ipfs/ 73 | sandbox/ 74 | **/node-db/ 75 | **/node-data/ 76 | **/pids/* 77 | !**/pids/keep 78 | 79 | # Scratch files 80 | scratch.ts 81 | scratch.js 82 | 83 | payondelivery/ 84 | *.tsbuildinfo 85 | 86 | # state file for tests 87 | test-state.json 88 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.15.1 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "printWidth": 120, 7 | "bracketSpacing": true 8 | } -------------------------------------------------------------------------------- /.remarkrc: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.14.1 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-567746: 7 | - '@xyo-network/sdk-base-nodejs > winston > async > lodash': 8 | patched: '2020-05-01T08:22:03.185Z' 9 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: XY | The Findables Company 3 | * @Date: Friday, 17th August 2018 9:56:29 am 4 | * @Email: developer@xyfindables.com 5 | * @Filename: launch.json 6 | 7 | * @Last modified time: Tuesday, 27th November 2018 11:14:49 am 8 | * @License: All Rights Reserved 9 | * @Copyright: Copyright XY | The Findables Company 10 | */ 11 | { 12 | "version": "0.2.0", 13 | "configurations": [ 14 | 15 | { 16 | "type": "node", 17 | "request": "attach", 18 | "name": "Attach by Process ID", 19 | "processId": "${command:PickProcess}" 20 | }, 21 | { 22 | "type": "node", 23 | "request": "launch", 24 | "name": "Run Tests", 25 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 26 | "args": ["--runInBand"], 27 | "console": "integratedTerminal", 28 | "internalConsoleOptions": "neverOpen", 29 | "env": { 30 | "NODE_ENV": "test" 31 | } 32 | }, 33 | { 34 | "type": "node", 35 | "request": "launch", 36 | "name": "Test Open File", 37 | "program": "${workspaceFolder}/node_modules/jest/bin/jest", 38 | "args": ["${relativeFile}", "--detectOpenHandles"], 39 | "console": "integratedTerminal", 40 | "internalConsoleOptions": "neverOpen", 41 | "env": { 42 | "NODE_ENV": "test" 43 | } 44 | }, 45 | { 46 | "name": "Current TS File", 47 | "type": "node", 48 | "request": "launch", 49 | "args": ["${relativeFile}"], 50 | "runtimeArgs": ["--nolazy", "-r", "ts-node/register"] 51 | }, 52 | { 53 | "name": "Launch Archivist", 54 | "type": "node", 55 | "request": "launch", 56 | "args": ["src/index.ts", "start", "archivist"], 57 | "cwd": "${workspaceFolder}/packages/app", 58 | "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], 59 | "console": "integratedTerminal" 60 | }, 61 | { 62 | "name": "Launch Dynamo Archivist", 63 | "type": "node", 64 | "request": "launch", 65 | "args": ["src/index.ts", "start", "archivist-dynamo"], 66 | "cwd": "${workspaceFolder}/packages/app", 67 | "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], 68 | "console": "integratedTerminal" 69 | }, 70 | { 71 | "name": "Launch Home", 72 | "type": "node", 73 | "request": "launch", 74 | "args": ["src/index.ts", "home"], 75 | "cwd": "${workspaceFolder}/packages/app", 76 | "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], 77 | "console": "integratedTerminal" 78 | }, 79 | { 80 | "name": "Launch Default", 81 | "type": "node", 82 | "request": "launch", 83 | "args": ["src/index.ts"], 84 | "cwd": "${workspaceFolder}/packages/app", 85 | "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], 86 | "console": "integratedTerminal" 87 | } 88 | ] 89 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "tslint.autoFixOnSave": true, 3 | "typescript.tsdk": "node_modules/typescript/lib", 4 | "psi-header.changes-tracking": { 5 | "isActive": true, 6 | "autoHeader": "manualSave" 7 | }, 8 | "psi-header.config": { 9 | "forceToTop": true, 10 | "blankLinesAfter": 1, 11 | "author": "XYO Development Team", 12 | "authorEmail": "support@xyo.network", 13 | "copyrightHolder": "XY - The Persistent Company" 14 | }, 15 | "psi-header.variables": [ 16 | ["projectCreationYear", "2017"] 17 | ], 18 | "psi-header.templates": [ 19 | { 20 | "language": "*", 21 | "template": [ 22 | "File: <>", 23 | "Project: <>", 24 | "File Created: <>", 25 | "Author: <> (<>)", 26 | "-----", 27 | "Last Modified: <>", 28 | "Modified By: <> (<>>)", 29 | "-----", 30 | "Copyright <> - <> <>" 31 | ] 32 | } 33 | ], 34 | "cSpell.words": [ 35 | "hasher", 36 | "repo" 37 | ], 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![logo]](https://xyo.network) 2 | 3 | # sdk-core-nodejs 4 | 5 | [![NPM](https://img.shields.io/npm/v/@xyo-network/sdk-core-nodejs.svg?style=plastic)](https://www.npmjs.com/package/@xyo-network/sdk-core-nodejs) 6 | 7 | ![](https://github.com/XYOracleNetwork/sdk-core-nodejs/workflows/Build/badge.svg?branch=develop) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/c0f40ecdba874362a2f212b032df8d86)](https://www.codacy.com/gh/XYOracleNetwork/sdk-core-nodejs?utm_source=github.com&utm_medium=referral&utm_content=XYOracleNetwork/sdk-core-nodejs&utm_campaign=Badge_Grade) [![Maintainability](https://api.codeclimate.com/v1/badges/f3dd4f4d35e1bd9eeabc/maintainability)](https://codeclimate.com/github/XYOracleNetwork/sdk-core-nodejs/maintainability) [![BCH compliance](https://bettercodehub.com/edge/badge/XYOracleNetwork/sdk-core-nodejs?branch=master)](https://bettercodehub.com/) [![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=XYOracleNetwork_sdk-core-nodejs&metric=alert_status)](https://sonarcloud.io/dashboard?id=XYOracleNetwork_sdk-core-nodejs) 8 | [![Known Vulnerabilities](https://snyk.io/test/github/XYOracleNetwork/sdk-core-nodejs/badge.svg?targetFile=package.json)](https://snyk.io/test/github/XYOracleNetwork/sdk-core-nodejs?targetFile=package.json) 9 | 10 | > The XYO Foundation provides this source code available in our efforts to advance the understanding of the XYO Procotol and its possible uses. We continue to maintain this software in the interest of developer education. Usage of this source code is not intended for production. 11 | 12 | ## Table of Contents 13 | 14 | - [Title](#sdk-core-nodejs) 15 | - [Project Overview](#project-overview) 16 | - [Scope of Features](#scope-of-features) 17 | - [Yellow Paper](#yellow-paper) 18 | - [Architecture and Design](#architecture-and-design) 19 | - [Getting Started](#getting-started) 20 | - [Developer Guide](#developer-guide) 21 | - [Maintainers](#maintainers) 22 | - [License](#license) 23 | - [Credits](#credits) 24 | 25 | ## Project Overview 26 | 27 | ### Scope of features 28 | 29 | Core functionality for the XYO NodeJS projects. This repository implements 30 | the core objects and services used in the XYO protocol. Additionally it provides core XYO features like performing bound-witnesses, hashing, signing, serialization, origin-chain management and TCP Network services. Alas, it exposes a number of CLI applications for running archivists and diviners. 31 | 32 | ### Yellow Paper 33 | 34 | The XYO protocol for creating origin-blocks is specified in the [XYO Yellow Paper](https://docs.xyo.network/XYO-Yellow-Paper.pdf). In it, it describes the behavior of how a node on the XYO network should create bound-witnesses. Note, the behavior is not coupled with any particular technology constraints around transport layers, cryptographic algorithms, or hashing algorithms. 35 | 36 | ### Architecture and Design 37 | 38 | As such, the design of the system is aimed at abstracting these concepts 39 | so that the concrete implementations of these components can be swapped out so long as they conform to the correct interfaces. 40 | 41 | Practically, this library uses TypeScript, which transpiles to JavaScript. Additionally, a TCP network provider has been implemented. Furthermore, some of the most popular public-key cryptography algorithms and hashing algorithms have been wrapped and made available to the core library. If you're favorite crypto signing algorithm is not yet supported, we welcome pull-requests and suggestions. 42 | 43 | [Here](https://github.com/XYOracleNetwork/spec-coreobjectmodel-tex) is a link to the core object model that contains an index of major/minor values and their respective objects. 44 | 45 | > If you are looking to create an XYO Node using a CLI, this is not the library you should refer to, please go to our [app-xyo-nodejs repo here](https://github.com/XYOracleNetwork/app-xyo-nodejs). This library provides core nodejs components for XYO Protocol functions, but it itself does not generate nodes for the Archivist or Diviner. 46 | 47 | ## Getting started 48 | 49 | If you intend yo bypass the node XYO app and integrate these core components into your app. 50 | 51 | ### Install as a dependency 52 | 53 | We prefer `yarn`, but feel free to use either `npm` or `yarn` 54 | 55 | `yarn` 56 | 57 | ```bash 58 | yarn add @xyo-network/sdk-core-nodejs 59 | ``` 60 | 61 | `npm` 62 | 63 | ```bash 64 | npm install @xyo-network/sdk-core-nodejs 65 | ``` 66 | 67 | Then based on how you have set up your project, import the components you are looking for, an example: 68 | 69 | ```javascript 70 | import { XyoOriginState } from '@xyo-network/sdk-core-nodejs' 71 | ``` 72 | 73 | Or multiple components 74 | 75 | ```javascript 76 | import { 77 | XyoOriginState, 78 | XyoFileOriginStateRepository 79 | } from '@xyo-network/sdk-core-nodejs' 80 | 81 | ``` 82 | 83 | 84 | ## Developer Guide 85 | 86 | Developers should conform to git flow workflow. Additionally, we should try to make sure 87 | every commit builds. Commit messages should be meaningful serve as a meta history for the 88 | repository. Please squash meaningless commits before submitting a pull-request. 89 | 90 | To contribute and test, the current workflow would be to use the XYO App SDK nodejs, see which methods may be failing, and then clone and test this core library. 91 | 92 | ### Clone repository 93 | 94 | ```sh 95 | git clone https://github.com/XYOracleNetwork/sdk-core-nodejs 96 | ``` 97 | 98 | ### Install dependencies 99 | 100 | After cloning the repository, change directory to the folder that houses the repository. 101 | 102 | ```sh 103 | cd sdk-core-nodejs 104 | ``` 105 | 106 | You will have to run Lerna to setup the links 107 | 108 | ```sh 109 | yarn install 110 | ``` 111 | 112 | ### Build 113 | 114 | Once the dependencies are installed run 115 | 116 | ```sh 117 | yarn build 118 | ``` 119 | 120 | ### Testing 121 | 122 | #### Run all tests 123 | 124 | ```sh 125 | yarn test 126 | ``` 127 | 128 | 129 | There is git hook on commits to validate the project builds. If you'd like to commit your changes 130 | while developing locally and want to skip this step you can use the `--no-verify` commit option. 131 | 132 | i.e. 133 | 134 | ```sh 135 | git commit --no-verify -m "COMMIT MSG" 136 | ``` 137 | 138 | ### Development Tools 139 | 140 | #### NVM (Node Version Manager) 141 | 142 | A number of the libraries that this project depends on may fail at install-time because they need to be built from C++ source where the output is specific to the host system. The underlying issue is that it is trying to modify files in protected areas of the file-system. 143 | 144 | This is all to say that [nvm](https://github.com/creationix/nvm) is strongly recommended for developers. Additionally you will find a [.nvmrc file](.nvmrc) at the root-level of the project that specifies the currently supported version of Node. 145 | 146 | #### Workflow 147 | 148 | The project structure was designed to support and encourage a modular architecture. This project uses a typical [Lerna](https://lernajs.io/) layout. That is, all modules are located in the [packages folder](packages). This allows for a couple of things that are conducive to an efficient development and release process: 149 | 150 | - Local linking of modules during development 151 | - Ability to release patches, minor, and major version upgrades to npm with ease 152 | - TypeScript linking using [project references](https://www.typescriptlang.org/docs/handbook/project-references.html) 153 | 154 | That said, since this is a TypeScript project, the source must be transpiled to JavaScript before execution. So, if a change is made to one or more modules in the package directory `yarn build` must be run from the project root for the changes to reflect. 155 | 156 | So a typical workflow might look like this: 157 | 158 | - Developer pulls down repository and changes directory in project root 159 | - Developer runs `yarn install` to install all dependencies of all the packages in accordance with [yarn workspace feature](https://yarnpkg.com/lang/en/docs/workspaces/) 160 | - Developer runs `yarn build` to transpile TypeScript source to JavaScript 161 | - Developer makes changes in one or more packages 162 | - Developer runs `yarn build` to see those changes reflected and linked accordingly 163 | - On occasion, running `yarn clean` may prove useful for resetting the project to clean state 164 | - When a change-set is complete and has gone through the proper code-review etc, a release can be made running `yarn release`. Release versions should follow [SemVer](https://semver.org/) standards. 165 | 166 | ##### Set the first account in ganache as environment variable for config 167 | 168 | ```sh 169 | eval `./scripts/manage-ganache.js set-account` 170 | ``` 171 | 172 | ## Maintainers 173 | 174 | - Carter Harrison 175 | - Arie Trouw 176 | - Kevin Weiler 177 | - Phillip Lorenzo 178 | 179 | ## License 180 | 181 | See the [LICENSE](LICENSE) file for license details. 182 | 183 | ## Credits 184 | 185 | Made with 🔥and ❄️ by [XYO](https://www.xyo.network) 186 | 187 | [logo]: https://cdn.xy.company/img/brand/XYO_full_colored.png 188 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/package", 3 | "name": "@xyo-network/sdk-core-nodejs", 4 | "version": "0.71.6", 5 | "description": "A workspace to aggregated nodejs XYO projects", 6 | "main": "dist/src/index.js", 7 | "types": "dist/src/index.d.ts", 8 | "author": "XY Development Team", 9 | "license": "LGPL-3.0", 10 | "scripts": { 11 | "start": "ts-node src/index.ts", 12 | "build": "tsc -p tsconfig.build.json && yarn lint", 13 | "lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint --ext .js,.ts ./src", 14 | "fix": "NODE_OPTIONS=--max_old_space_size=8192 eslint --ext .js,.ts ./src --fix", 15 | "clean": "rm -r -f ./node_modules && rm -r -f ./build", 16 | "reinstall": "rm -r -f ./node_modules && rm -f ./yarn.lock && rm -f ./yarn-error.log && yarn install && yarn outdated", 17 | "test": "yarn licensecheck && export NODE_ENV=test && yarn jest --no-cache --forceExit --coverage --verbose false --detectOpenHandles", 18 | "start:tcp": "yarn build && node --nolazy -r ts-node/register examples/tcp-server/index.ts", 19 | "snyk-protect": "snyk protect", 20 | "prepare": "yarn run snyk-protect" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/XYOracleNetwork/sdk-core-nodejs.git" 25 | }, 26 | "devDependencies": { 27 | "@types/bs58": "^4.0.1", 28 | "@types/elliptic": "^6.4.8", 29 | "@types/jest": "^26.0.22", 30 | "@typescript-eslint/eslint-plugin": "^4.22.0", 31 | "@typescript-eslint/parser": "^4.22.0", 32 | "enquirer": "^2.3.6", 33 | "eslint": "^7.24.0", 34 | "eslint-config-prettier": "^8.2.0", 35 | "eslint-plugin-import": "^2.22.1", 36 | "eslint-plugin-json": "^2.1.2", 37 | "eslint-plugin-no-secrets": "^0.8.9", 38 | "eslint-plugin-prettier": "^3.4.0", 39 | "eslint-plugin-react": "^7.23.2", 40 | "eslint-plugin-react-hooks": "^4.1.2", 41 | "eslint-plugin-simple-import-sort": "^7.0.0", 42 | "eslint-plugin-sort-export-all": "^1.1.1", 43 | "eslint-plugin-sort-keys-fix": "^1.1.1", 44 | "eslint-plugin-typescript-sort-keys": "^1.6.0", 45 | "eslint-plugin-workspaces": "^0.6.0", 46 | "jest": "^26.6.3", 47 | "license-checker": "^25.0.1", 48 | "prettier": "^2.2.1", 49 | "shelljs": "^0.8.4", 50 | "snyk": "^1.543.0", 51 | "ts-jest": "^26.5.5", 52 | "ts-node": "9.1.1", 53 | "typescript": "^4.2.4" 54 | }, 55 | "dependencies": { 56 | "@xyo-network/sdk-base-nodejs": "^0.7.6", 57 | "bs58": "4.0.1", 58 | "delay": "^5.0.0", 59 | "elliptic": "6.5.4" 60 | }, 61 | "prettier": { 62 | "semi": false, 63 | "singleQuote": true, 64 | "jsxSingleQuote": true 65 | }, 66 | "files": [ 67 | "dist", 68 | "bin" 69 | ], 70 | "engineStrict": true, 71 | "engines": { 72 | "node": ">=8.0.0" 73 | }, 74 | "snyk": true 75 | } 76 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | /* 2 | * File: sonar-project.properties 3 | * Project: xyo-core 4 | * File Created: Wednesday, 17th April 2019 5:56:44 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Wednesday, 17th April 2019 9:10:34 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2019 XY - The Persistent Company 11 | */ 12 | 13 | sonar.projectKey=XYOracleNetwork_sdk-core-nodejs 14 | sonar.projectName=sdk-core-nodejs 15 | 16 | # ===================================================== 17 | # Meta-data for the project 18 | # ===================================================== 19 | 20 | sonar.links.homepage=https://github.com/XYOracleNetwork/sdk-core-nodejs 21 | sonar.links.ci=https://github.com/XYOracleNetwork/sdk-core-nodejs 22 | sonar.links.scm=https://github.com/XYOracleNetwork/sdk-core-nodejs 23 | sonar.links.issue=https://github.com/XYOracleNetwork/sdk-core-nodejs/issues 24 | 25 | 26 | # ===================================================== 27 | # Properties that will be shared amongst all modules 28 | # ===================================================== 29 | 30 | sonar.host.url=https://sonarcloud.io 31 | sonar.organization=xyo-network 32 | sonar.sources=. 33 | sonar.exclusions=node_modules/**/* 34 | sonar.login=${SONAR_TOKEN} 35 | 36 | # ===================================================== 37 | # TypeScript config 38 | # ===================================================== 39 | sonar.typescript.tsconfigPath=./tsconfig.json -------------------------------------------------------------------------------- /src/bound-witness/index.ts: -------------------------------------------------------------------------------- 1 | import XyoBoundWitnessHander from './xyo-bound-witness-handler' 2 | 3 | export { XyoBoundWitness } from './xyo-bound-witness' 4 | export { XyoZigZagBoundWitness } from './xyo-zig-zag-bound-witness' 5 | export { XyoZigZagBoundWitnessHander } from './xyo-zig-zag-bound-witness-handler' 6 | export { XyoZigZagBoundWitnessSession } from './xyo-zig-zag-bound-witness-session' 7 | 8 | export { XyoBoundWitnessHander } 9 | -------------------------------------------------------------------------------- /src/bound-witness/spec/xyo-bound-witness.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer } from '../../object-model' 2 | import { XyoBoundWitness } from '../xyo-bound-witness' 3 | 4 | describe('XyoBoundWitness', () => { 5 | it('Not completed', () => { 6 | // tslint:disable-next-line:max-line-length 7 | const bytes = Buffer.from( 8 | '2002DE201547201944000C41B76AE59BB079817B8735E3E216D68E991F2B4F86E982C2DA635B779265E904E5D8E26756E67B72077F510AFF37F5C9EC04F9A1B16C73B07E1F1CA299BA9CFAF4201547201944000C41B946583242B36DDB3650FED6617A12E68DE7D80A686DF84F5FD268122502AA8FC3B873B38B6F4637009593BB9DC32AF2EC88DBC7841DF18EEB540FE21E4969BC201749201A46000943203AC4736260B427B2895250E15BC73B27AD2B3EE78460172F7CD856CFD15AE67D205911C0CDD962616BAC95D9AB4DCE689AF0826A0D92FB4980F2ADBB089844340A', 9 | 'hex' 10 | ) 11 | const buffer = new XyoBuffer(bytes) 12 | const boundWitness = new XyoBoundWitness(buffer) 13 | 14 | expect(boundWitness.getIsCompleted()).toBe(false) 15 | }) 16 | 17 | it('Is completed', () => { 18 | // tslint:disable-next-line:max-line-length 19 | const bytes = Buffer.from( 20 | '6002012B201547201944000C4192BAF8FBA41F6B5CA997DF7634F1F33176E0DDA8F7B485C6CD2EBC3BA06D4EEC8BB98284DB33761BA8A7668D1A5C140384968A0BE3436067F10A0D6B7F5AAFFF201547201944000C41ED1512DA596726D8E19A592BBA5573D31174C424FDFD7A0D14B3088BD22F0EB520F99E19D78DBBD613B79277FEB2BD0911C4C379E69B8688CC744B5B5ACF928F20174A201A470009442100CAC1C5F12BCCEA80C176FCCEEFEC616E86A9F208F43E45D49E8F32F76278B9F8202ABFC11D935F56D5CFECFDC66D4CA37D67C69AE6CD3C1DB41794C3C7FF41FE90201749201A4600094320656984EF23EAD4304E4A1AB3321F64BF9629FFE0E3A4097B181C2295892578D2205B90DAD8607D3BE600209771E2A19EC9EA3BB7BEE9D44A99395E85577FBCDBB7', 21 | 'hex' 22 | ) 23 | const buffer = new XyoBuffer(bytes) 24 | const boundWitness = new XyoBoundWitness(buffer) 25 | 26 | expect(boundWitness.getIsCompleted()).toBe(true) 27 | }) 28 | 29 | it('Number of parties', () => { 30 | // tslint:disable-next-line:max-line-length 31 | const bytes = Buffer.from( 32 | '6002012B201547201944000C4192BAF8FBA41F6B5CA997DF7634F1F33176E0DDA8F7B485C6CD2EBC3BA06D4EEC8BB98284DB33761BA8A7668D1A5C140384968A0BE3436067F10A0D6B7F5AAFFF201547201944000C41ED1512DA596726D8E19A592BBA5573D31174C424FDFD7A0D14B3088BD22F0EB520F99E19D78DBBD613B79277FEB2BD0911C4C379E69B8688CC744B5B5ACF928F20174A201A470009442100CAC1C5F12BCCEA80C176FCCEEFEC616E86A9F208F43E45D49E8F32F76278B9F8202ABFC11D935F56D5CFECFDC66D4CA37D67C69AE6CD3C1DB41794C3C7FF41FE90201749201A4600094320656984EF23EAD4304E4A1AB3321F64BF9629FFE0E3A4097B181C2295892578D2205B90DAD8607D3BE600209771E2A19EC9EA3BB7BEE9D44A99395E85577FBCDBB7', 33 | 'hex' 34 | ) 35 | const buffer = new XyoBuffer(bytes) 36 | const boundWitness = new XyoBoundWitness(buffer) 37 | 38 | expect(boundWitness.getNumberOfFetters()).toBe(2) 39 | expect(boundWitness.getNumberOfWitnesses()).toBe(2) 40 | expect(boundWitness.getNumberOfParties()).toBe(2) 41 | }) 42 | 43 | it('Get fetter', () => { 44 | // tslint:disable-next-line:max-line-length 45 | const fetter0Bytes = Buffer.from( 46 | '201547201944000C4192BAF8FBA41F6B5CA997DF7634F1F33176E0DDA8F7B485C6CD2EBC3BA06D4EEC8BB98284DB33761BA8A7668D1A5C140384968A0BE3436067F10A0D6B7F5AAFFF', 47 | 'hex' 48 | ) 49 | // tslint:disable-next-line:max-line-length 50 | const fetter1Bytes = Buffer.from( 51 | '201547201944000C41ED1512DA596726D8E19A592BBA5573D31174C424FDFD7A0D14B3088BD22F0EB520F99E19D78DBBD613B79277FEB2BD0911C4C379E69B8688CC744B5B5ACF928F', 52 | 'hex' 53 | ) 54 | // tslint:disable-next-line:max-line-length 55 | const bytes = Buffer.from( 56 | '6002012B201547201944000C4192BAF8FBA41F6B5CA997DF7634F1F33176E0DDA8F7B485C6CD2EBC3BA06D4EEC8BB98284DB33761BA8A7668D1A5C140384968A0BE3436067F10A0D6B7F5AAFFF201547201944000C41ED1512DA596726D8E19A592BBA5573D31174C424FDFD7A0D14B3088BD22F0EB520F99E19D78DBBD613B79277FEB2BD0911C4C379E69B8688CC744B5B5ACF928F20174A201A470009442100CAC1C5F12BCCEA80C176FCCEEFEC616E86A9F208F43E45D49E8F32F76278B9F8202ABFC11D935F56D5CFECFDC66D4CA37D67C69AE6CD3C1DB41794C3C7FF41FE90201749201A4600094320656984EF23EAD4304E4A1AB3321F64BF9629FFE0E3A4097B181C2295892578D2205B90DAD8607D3BE600209771E2A19EC9EA3BB7BEE9D44A99395E85577FBCDBB7', 57 | 'hex' 58 | ) 59 | const buffer = new XyoBuffer(bytes) 60 | const boundWitness = new XyoBoundWitness(buffer) 61 | 62 | const fetter0 = boundWitness 63 | .getFetterOfParty(0) 64 | ?.getAll() 65 | .getContentsCopy() 66 | .toString('hex') 67 | const fetter1 = boundWitness 68 | .getFetterOfParty(1) 69 | ?.getAll() 70 | .getContentsCopy() 71 | .toString('hex') 72 | 73 | expect(fetter0).toBe(fetter0Bytes.toString('hex')) 74 | expect(fetter1).toBe(fetter1Bytes.toString('hex')) 75 | }) 76 | 77 | it('Get signing data', () => { 78 | // tslint:disable-next-line:max-line-length 79 | const signingData = Buffer.from( 80 | '201547201944000C4192BAF8FBA41F6B5CA997DF7634F1F33176E0DDA8F7B485C6CD2EBC3BA06D4EEC8BB98284DB33761BA8A7668D1A5C140384968A0BE3436067F10A0D6B7F5AAFFF201547201944000C41ED1512DA596726D8E19A592BBA5573D31174C424FDFD7A0D14B3088BD22F0EB520F99E19D78DBBD613B79277FEB2BD0911C4C379E69B8688CC744B5B5ACF928F', 81 | 'hex' 82 | ) 83 | // tslint:disable-next-line:max-line-length 84 | const bytes = Buffer.from( 85 | '6002012B201547201944000C4192BAF8FBA41F6B5CA997DF7634F1F33176E0DDA8F7B485C6CD2EBC3BA06D4EEC8BB98284DB33761BA8A7668D1A5C140384968A0BE3436067F10A0D6B7F5AAFFF201547201944000C41ED1512DA596726D8E19A592BBA5573D31174C424FDFD7A0D14B3088BD22F0EB520F99E19D78DBBD613B79277FEB2BD0911C4C379E69B8688CC744B5B5ACF928F20174A201A470009442100CAC1C5F12BCCEA80C176FCCEEFEC616E86A9F208F43E45D49E8F32F76278B9F8202ABFC11D935F56D5CFECFDC66D4CA37D67C69AE6CD3C1DB41794C3C7FF41FE90201749201A4600094320656984EF23EAD4304E4A1AB3321F64BF9629FFE0E3A4097B181C2295892578D2205B90DAD8607D3BE600209771E2A19EC9EA3BB7BEE9D44A99395E85577FBCDBB7', 86 | 'hex' 87 | ) 88 | const buffer = new XyoBuffer(bytes) 89 | const boundWitness = new XyoBoundWitness(buffer) 90 | 91 | const madeSigningData = boundWitness.getSigningData().toString('hex') 92 | 93 | expect(madeSigningData).toBe(signingData.toString('hex')) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /src/bound-witness/spec/xyo-zig-zag-bound-witness.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoSecp2556k1 } from '../../signing/ecdsa/xyo-secp256k1' 2 | import { XyoZigZagBoundWitness } from '../xyo-zig-zag-bound-witness' 3 | 4 | describe('XyoZigZagBoundWitness', () => { 5 | it('Self Sign Block', () => { 6 | const signers = [new XyoSecp2556k1()] 7 | const boundWitness = new XyoZigZagBoundWitness(signers, [], []) 8 | 9 | boundWitness.incomingData(undefined, true) 10 | 11 | expect(boundWitness.getIsCompleted()).toBe(true) 12 | expect(boundWitness.getNumberOfParties()).toBe(1) 13 | expect(boundWitness.getNumberOfWitnesses()).toBe(1) 14 | expect(boundWitness.getNumberOfFetters()).toBe(1) 15 | }) 16 | 17 | it('2 Party Block', () => { 18 | const signersAlice = [new XyoSecp2556k1()] 19 | const signersBob = [new XyoSecp2556k1()] 20 | const boundWitnessAlice = new XyoZigZagBoundWitness(signersAlice, [], []) 21 | const boundWitnessBob = new XyoZigZagBoundWitness(signersBob, [], []) 22 | 23 | const aliceToBobOne = boundWitnessAlice.incomingData(undefined, false) 24 | const bobToAliceOne = boundWitnessBob.incomingData(aliceToBobOne, true) 25 | const aliceToBobTwo = boundWitnessAlice.incomingData(bobToAliceOne, false) 26 | boundWitnessBob.incomingData(aliceToBobTwo, false) 27 | 28 | expect(boundWitnessAlice.getIsCompleted()).toBe(true) 29 | expect(boundWitnessAlice.getNumberOfParties()).toBe(2) 30 | expect(boundWitnessAlice.getNumberOfWitnesses()).toBe(2) 31 | expect(boundWitnessAlice.getNumberOfFetters()).toBe(2) 32 | 33 | expect(boundWitnessBob.getIsCompleted()).toBe(true) 34 | expect(boundWitnessBob.getNumberOfParties()).toBe(2) 35 | expect(boundWitnessBob.getNumberOfWitnesses()).toBe(2) 36 | expect(boundWitnessBob.getNumberOfFetters()).toBe(2) 37 | 38 | const aliceBytes = boundWitnessAlice 39 | .getAll() 40 | .getContentsCopy() 41 | .toString('hex') 42 | const bobBytes = boundWitnessBob.getAll().getContentsCopy().toString('hex') 43 | 44 | expect(aliceBytes).toEqual(bobBytes) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /src/bound-witness/xyo-bound-witness-handler.ts: -------------------------------------------------------------------------------- 1 | import { XyoNetworkHandler } from '../network/xyo-network-handler' 2 | import XyoProcedureCatalog from '../network/xyo-procedure-catalog' 3 | import XyoSigner from '../signing/xyo-signer' 4 | import { XyoBoundWitness } from './xyo-bound-witness' 5 | 6 | abstract class XyoBoundWitnessHander { 7 | abstract boundWitness( 8 | handler: XyoNetworkHandler, 9 | catalog: XyoProcedureCatalog, 10 | signers: XyoSigner[] 11 | ): Promise 12 | } 13 | 14 | export default XyoBoundWitnessHander 15 | -------------------------------------------------------------------------------- /src/bound-witness/xyo-bound-witness-validator.ts: -------------------------------------------------------------------------------- 1 | import { XyoSignatureVerify } from '../signing/xyo-signer' 2 | import { XyoBoundWitness } from './xyo-bound-witness' 3 | 4 | export class XyoBoundWitnessValidator { 5 | private signatureValidators: Map 6 | 7 | constructor(signatureValidators: Map) { 8 | this.signatureValidators = signatureValidators 9 | } 10 | 11 | public validate( 12 | boundWitness: XyoBoundWitness, 13 | allowUnknownSignatures: boolean 14 | ): boolean { 15 | const signingData = boundWitness.getSigningData() 16 | const publicKeysParty = boundWitness.getPublicKeys() 17 | const signaturesParty = boundWitness.getSignatures() 18 | 19 | if (publicKeysParty.length !== signaturesParty.length) { 20 | return false 21 | } 22 | 23 | for (let i = 0; i < publicKeysParty.length; i++) { 24 | const publicKeys = publicKeysParty[i] 25 | const signatures = signaturesParty[i] 26 | 27 | if (publicKeysParty.length !== signaturesParty.length) { 28 | return false 29 | } 30 | 31 | for (let j = 0; j < publicKeys.length; j++) { 32 | const publicKey = publicKeys[j] 33 | const signature = signatures[j] 34 | const signatureValidator = this.signatureValidators.get( 35 | signature.getSchema().id 36 | ) 37 | 38 | if (signatureValidator) { 39 | if ( 40 | !signatureValidator( 41 | publicKey.getAll().getContentsCopy(), 42 | signature.getAll().getContentsCopy(), 43 | signingData 44 | ) 45 | ) { 46 | return false 47 | } 48 | } 49 | if (signatureValidator && !allowUnknownSignatures) { 50 | return false 51 | } 52 | } 53 | } 54 | 55 | return true 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/bound-witness/xyo-bound-witness.ts: -------------------------------------------------------------------------------- 1 | import { XyoHasher } from '../hashing' 2 | import { XyoIterableStructure, XyoSchema, XyoStructure } from '../object-model' 3 | import { XyoObjectSchema } from '../schema' 4 | import { XyoSigner } from '../signing' 5 | 6 | export class XyoBoundWitness extends XyoIterableStructure { 7 | public static createMasterArrayWithSubArray( 8 | masterSchema: XyoSchema, 9 | subSchema: XyoSchema, 10 | masterItems: XyoStructure[], 11 | subItems: XyoStructure[] 12 | ): XyoIterableStructure { 13 | const sub = XyoIterableStructure.newIterable(subSchema, subItems) 14 | const itemsInMaster: XyoStructure[] = [sub] 15 | 16 | for (const materItem of masterItems) { 17 | itemsInMaster.push(materItem) 18 | } 19 | 20 | return XyoIterableStructure.newIterable(masterSchema, itemsInMaster) 21 | } 22 | 23 | public getIsCompleted(): boolean { 24 | if (this.getId(XyoObjectSchema.WITNESS.id).length !== 0) { 25 | return ( 26 | this.getId(XyoObjectSchema.WITNESS.id).length === 27 | this.getId(XyoObjectSchema.FETTER.id).length 28 | ) 29 | } 30 | return false 31 | } 32 | 33 | // [party index][key index] 34 | public getPublicKeys(): XyoStructure[][] { 35 | const keysets: XyoStructure[][] = [] 36 | const fetters = this.getId( 37 | XyoObjectSchema.FETTER.id 38 | ) as XyoIterableStructure[] 39 | 40 | for (const fetter of fetters) { 41 | const keyset: XyoStructure[] = [] 42 | const partyKeySets = fetter.getId( 43 | XyoObjectSchema.KEY_SET.id 44 | ) as XyoIterableStructure[] 45 | 46 | for (const partyKeySet of partyKeySets) { 47 | const keysetIt = partyKeySet.newIterator() 48 | while (keysetIt.hasNext()) { 49 | keyset.push(keysetIt.next().value) 50 | } 51 | } 52 | keysets.push(keyset) 53 | } 54 | 55 | return keysets 56 | } 57 | 58 | // [party index][key index] 59 | public getSignatures(): XyoStructure[][] { 60 | const signatureSets: XyoStructure[][] = [] 61 | const witnesses = this.getId( 62 | XyoObjectSchema.WITNESS.id 63 | ) as XyoIterableStructure[] 64 | 65 | for (const fetter of witnesses) { 66 | const signatureSet: XyoStructure[] = [] 67 | const partySignatureSets = fetter.getId( 68 | XyoObjectSchema.SIGNATURE_SET.id 69 | ) as XyoIterableStructure[] 70 | 71 | for (const partySignatureSet of partySignatureSets) { 72 | const signatureSetIt = partySignatureSet.newIterator() 73 | while (signatureSetIt.hasNext()) { 74 | signatureSet.push(signatureSetIt.next().value) 75 | } 76 | } 77 | signatureSets.push(signatureSet) 78 | } 79 | 80 | return signatureSets 81 | } 82 | 83 | // [party index][key index] 84 | public getHeuristics(): XyoStructure[][] { 85 | const heuristics: XyoStructure[][] = [] 86 | const fetters = this.getId( 87 | XyoObjectSchema.FETTER.id 88 | ) as XyoIterableStructure[] 89 | 90 | for (const fetter of fetters) { 91 | const partyHeuristics: XyoStructure[] = [] 92 | const fetterIt = fetter.newIterator() 93 | while (fetterIt.hasNext()) { 94 | partyHeuristics.push(fetterIt.next().value) 95 | } 96 | heuristics.push(partyHeuristics) 97 | } 98 | 99 | return heuristics 100 | } 101 | 102 | public getNumberOfFetters(): number { 103 | return this.getId(XyoObjectSchema.FETTER.id).length 104 | } 105 | 106 | public getNumberOfWitnesses(): number { 107 | return this.getId(XyoObjectSchema.WITNESS.id).length 108 | } 109 | 110 | public getHash(hasher: XyoHasher): XyoStructure { 111 | return hasher.hash(this.getSigningData()) 112 | } 113 | 114 | public sign(signer: XyoSigner): XyoStructure { 115 | return signer.sign(this.getSigningData()) 116 | } 117 | 118 | public getSigningData(): Buffer { 119 | const bounds = this.getWitnessFetterBoundary() 120 | return this.getValue().getContentsCopy().slice(0, bounds) 121 | } 122 | 123 | public getNumberOfParties(): number | undefined { 124 | const numberOfFetters = this.getNumberOfFetters() 125 | const numberOfWitnesses = this.getNumberOfWitnesses() 126 | 127 | if (numberOfFetters === numberOfWitnesses) { 128 | return numberOfWitnesses 129 | } 130 | 131 | return undefined 132 | } 133 | 134 | public getFetterOfParty( 135 | partyIndex: number 136 | ): XyoIterableStructure | undefined { 137 | const numberOfParties = this.getNumberOfParties() 138 | 139 | if (numberOfParties) { 140 | if (numberOfParties <= partyIndex) { 141 | return undefined 142 | } 143 | 144 | return this.get(partyIndex) as XyoIterableStructure 145 | } 146 | 147 | return undefined 148 | } 149 | 150 | public getWitnessOfParty( 151 | partyIndex: number 152 | ): XyoIterableStructure | undefined { 153 | const numberOfParties = this.getNumberOfParties() 154 | 155 | if (numberOfParties) { 156 | if (numberOfParties <= partyIndex) { 157 | return undefined 158 | } 159 | 160 | return this.get( 161 | numberOfParties * 2 - (partyIndex + 1) 162 | ) as XyoIterableStructure 163 | } 164 | 165 | return undefined 166 | } 167 | 168 | protected addToLedger(item: XyoStructure) { 169 | if (this.getIsCompleted()) { 170 | throw new Error('Bound witness is completed') 171 | } 172 | 173 | this.addElement(item) 174 | } 175 | 176 | private getWitnessFetterBoundary(): number { 177 | const fetters = this.getId(XyoObjectSchema.FETTER.id) 178 | let offsetIndex = 0 179 | 180 | for (const fetter of fetters) { 181 | offsetIndex += fetter.getAll().getSize() 182 | } 183 | 184 | return offsetIndex 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/bound-witness/xyo-zig-zag-bound-witness-handler.ts: -------------------------------------------------------------------------------- 1 | import { XyoCatalogFlags } from '../network/xyo-catalog-flags' 2 | import { XyoChoicePacket } from '../network/xyo-choice-packet' 3 | import { XyoNetworkHandler } from '../network/xyo-network-handler' 4 | import XyoProcedureCatalog from '../network/xyo-procedure-catalog' 5 | import { XyoBuffer, XyoIterableStructure } from '../object-model' 6 | import XyoPayloadConstructor from '../origin/xyo-payload-constructor' 7 | import { XyoSigner } from '../signing' 8 | import { XyoBoundWitness } from './xyo-bound-witness' 9 | import XyoBoundWitnessHander from './xyo-bound-witness-handler' 10 | import { XyoZigZagBoundWitnessSession } from './xyo-zig-zag-bound-witness-session' 11 | 12 | export class XyoZigZagBoundWitnessHander extends XyoBoundWitnessHander { 13 | private payloadProvider: XyoPayloadConstructor 14 | private currentBoundWitnessSession: XyoZigZagBoundWitnessSession | undefined 15 | 16 | constructor(payloadProvider: XyoPayloadConstructor) { 17 | super() 18 | this.payloadProvider = payloadProvider 19 | } 20 | 21 | public async boundWitness( 22 | handler: XyoNetworkHandler, 23 | catalogue: XyoProcedureCatalog, 24 | signers: XyoSigner[] 25 | ): Promise { 26 | try { 27 | if (this.currentBoundWitnessSession !== undefined) { 28 | throw new Error('Bound witness is already in session') 29 | } 30 | 31 | const initData = handler.pipe.getInitiationData() 32 | 33 | if (initData) { 34 | const serverChoice = catalogue.choose(initData.getChoice()) 35 | const cBw = await this.handleBoundWitness( 36 | undefined, 37 | handler, 38 | XyoCatalogFlags.flip(serverChoice), 39 | signers 40 | ) 41 | return cBw 42 | } 43 | 44 | const response = await handler.sendCatalogPacket( 45 | catalogue.getEncodedCanDo() 46 | ) 47 | 48 | if (!response) { 49 | throw new Error('Response is undefined') 50 | } 51 | 52 | const adv = new XyoChoicePacket(response) 53 | const startingData = new XyoIterableStructure( 54 | new XyoBuffer(adv.getResponse()) 55 | ) 56 | const choice = adv.getChoice() 57 | const bw = await this.handleBoundWitness( 58 | startingData, 59 | handler, 60 | choice, 61 | signers 62 | ) 63 | return bw 64 | } catch (error) { 65 | this.currentBoundWitnessSession = undefined 66 | return undefined 67 | } 68 | } 69 | 70 | private async handleBoundWitness( 71 | startingData: XyoIterableStructure | undefined, 72 | handler: XyoNetworkHandler, 73 | choice: Buffer, 74 | signers: XyoSigner[] 75 | ): Promise { 76 | const payloads = await this.payloadProvider.getPayloads(choice) 77 | const boundWitness = new XyoZigZagBoundWitnessSession( 78 | handler, 79 | payloads.signed, 80 | payloads.unsigned, 81 | signers, 82 | XyoCatalogFlags.flip(choice) 83 | ) 84 | this.currentBoundWitnessSession = boundWitness 85 | 86 | await boundWitness.doBoundWitness(startingData) 87 | 88 | this.currentBoundWitnessSession = undefined 89 | 90 | return boundWitness 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/bound-witness/xyo-zig-zag-bound-witness-session.ts: -------------------------------------------------------------------------------- 1 | import { XyoNetworkHandler } from '../network/xyo-network-handler' 2 | import { XyoBuffer, XyoIterableStructure, XyoStructure } from '../object-model' 3 | import { XyoSigner } from '../signing' 4 | import { XyoZigZagBoundWitness } from './xyo-zig-zag-bound-witness' 5 | 6 | export class XyoZigZagBoundWitnessSession extends XyoZigZagBoundWitness { 7 | private handler: XyoNetworkHandler 8 | private choice: Buffer 9 | private cycles = 0 10 | 11 | constructor( 12 | handler: XyoNetworkHandler, 13 | signedPayload: XyoStructure[], 14 | unsignedPayload: XyoStructure[], 15 | signers: XyoSigner[], 16 | choice: Buffer 17 | ) { 18 | super(signers, signedPayload, unsignedPayload) 19 | 20 | this.handler = handler 21 | this.choice = choice 22 | } 23 | 24 | public async doBoundWitness( 25 | transfer: XyoIterableStructure | undefined 26 | ): Promise { 27 | if (!this.getIsCompleted()) { 28 | const response = await this.sendAndReceive( 29 | transfer !== undefined, 30 | transfer 31 | ) 32 | 33 | if (this.cycles === 0 && transfer !== undefined && response !== null) { 34 | this.incomingData(response, false) 35 | } else { 36 | this.cycles++ 37 | return this.doBoundWitness(response) 38 | } 39 | } 40 | } 41 | 42 | private async sendAndReceive( 43 | didHaveData: boolean, 44 | transfer: XyoIterableStructure | undefined 45 | ) { 46 | let response: Buffer | undefined 47 | const returnData = this.incomingData( 48 | transfer, 49 | this.cycles === 0 && didHaveData 50 | ) 51 | 52 | if (this.cycles === 0 && !didHaveData) { 53 | response = await this.handler.sendChoicePacket( 54 | this.choice, 55 | returnData.getAll().getContentsCopy() 56 | ) 57 | } else { 58 | response = await this.handler.pipe.send( 59 | returnData.getAll().getContentsCopy(), 60 | this.cycles === 0 61 | ) 62 | 63 | if (this.cycles === 0 && !response) { 64 | throw new Error('Response is null') 65 | } 66 | } 67 | 68 | if (response) { 69 | return new XyoIterableStructure(new XyoBuffer(response)) 70 | } 71 | 72 | return undefined 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/bound-witness/xyo-zig-zag-bound-witness.ts: -------------------------------------------------------------------------------- 1 | import { XyoIterableStructure, XyoStructure } from '../object-model' 2 | import { XyoObjectSchema } from '../schema' 3 | import { XyoSigner } from '../signing' 4 | import { XyoBoundWitness } from './xyo-bound-witness' 5 | 6 | export class XyoZigZagBoundWitness extends XyoBoundWitness { 7 | private signers: XyoSigner[] 8 | private signedPayload: XyoStructure[] 9 | private unsignedPayload: XyoStructure[] 10 | private hasSentFetter = false 11 | 12 | constructor( 13 | signers: XyoSigner[], 14 | signedPayload: XyoStructure[], 15 | unsignedPayload: XyoStructure[] 16 | ) { 17 | super(XyoIterableStructure.newIterable(XyoObjectSchema.BW, []).getAll()) 18 | 19 | this.signers = signers 20 | this.signedPayload = signedPayload 21 | this.unsignedPayload = unsignedPayload 22 | } 23 | 24 | public incomingData( 25 | transfer: XyoIterableStructure | undefined, 26 | endpoint: boolean 27 | ): XyoIterableStructure { 28 | if (transfer) { 29 | this.addTransfer(transfer) 30 | } 31 | 32 | if (!this.hasSentFetter) { 33 | const fetter = XyoBoundWitness.createMasterArrayWithSubArray( 34 | XyoObjectSchema.FETTER, 35 | XyoObjectSchema.KEY_SET, 36 | this.signedPayload, 37 | this.getPublicKeysOfSigners() 38 | ) 39 | 40 | this.addToLedger(fetter) 41 | this.hasSentFetter = true 42 | } 43 | 44 | if (this.getNumberOfFetters() !== this.getNumberOfWitnesses()) { 45 | return this.getReturnFromIncoming( 46 | this.getNumberOfWitnessesFromTransfer(transfer), 47 | endpoint 48 | ) 49 | } 50 | 51 | return this.encodeTransfer([]) 52 | } 53 | 54 | private getNumberOfWitnessesFromTransfer( 55 | transfer: XyoIterableStructure | undefined 56 | ) { 57 | if (transfer) { 58 | return transfer.getId(XyoObjectSchema.WITNESS.id).length 59 | } 60 | 61 | return 0 62 | } 63 | 64 | private getReturnFromIncoming(numberOfWitnesses: number, endpoint: boolean) { 65 | if (numberOfWitnesses === 0 && !endpoint) { 66 | const elements: XyoStructure[] = [] 67 | const it = this.newIterator() 68 | 69 | while (it.hasNext()) { 70 | elements.push(it.next().value) 71 | } 72 | 73 | return this.encodeTransfer(elements) 74 | } 75 | 76 | return this.passAndSign(numberOfWitnesses) 77 | } 78 | 79 | private passAndSign(numberOfWitnesses: number): XyoIterableStructure { 80 | const toSendBack: XyoStructure[] = [] 81 | 82 | this.signBoundWitness(this.unsignedPayload) 83 | 84 | const fetters = this.getId(XyoObjectSchema.FETTER.id) 85 | const witnesses = this.getId(XyoObjectSchema.WITNESS.id) 86 | 87 | const x = numberOfWitnesses + 1 88 | const y = fetters.length - 1 89 | 90 | if (x <= y) { 91 | for (let i = x; i <= y; i++) { 92 | toSendBack.push(fetters[i]) 93 | } 94 | } 95 | 96 | toSendBack.push(witnesses[witnesses.length - 1]) 97 | 98 | return this.encodeTransfer(toSendBack) 99 | } 100 | 101 | private encodeTransfer(items: XyoStructure[]) { 102 | const fetters: XyoStructure[] = [] 103 | const witness: XyoStructure[] = [] 104 | 105 | for (const item of items) { 106 | switch (item.getSchema().id) { 107 | case XyoObjectSchema.FETTER.id: 108 | fetters.push(item) 109 | break 110 | case XyoObjectSchema.WITNESS.id: 111 | witness.push(item) 112 | break 113 | default: 114 | throw new Error('Must be fetter or witness') 115 | } 116 | } 117 | 118 | return this.encodeFettersAndWitnessesForTransfer(fetters, witness, items) 119 | } 120 | 121 | private encodeFettersAndWitnessesForTransfer( 122 | fetters: XyoStructure[], 123 | witness: XyoStructure[], 124 | items: XyoStructure[] 125 | ): XyoIterableStructure { 126 | if (fetters.length === 0 && witness.length !== 0) { 127 | return XyoIterableStructure.newIterable( 128 | XyoObjectSchema.WITNESS_SET, 129 | witness 130 | ) 131 | } 132 | 133 | if (fetters.length !== 0 && witness.length === 0) { 134 | return XyoIterableStructure.newIterable( 135 | XyoObjectSchema.FETTER_SET, 136 | fetters 137 | ) 138 | } 139 | 140 | return XyoIterableStructure.newIterable(XyoObjectSchema.BW_FRAGMENT, items) 141 | } 142 | 143 | private addTransfer(transfer: XyoIterableStructure) { 144 | XyoIterableStructure.validate(transfer) 145 | 146 | const it = transfer.newIterator() 147 | 148 | while (it.hasNext()) { 149 | this.addToLedger(it.next().value) 150 | } 151 | } 152 | 153 | private getPublicKeysOfSigners(): XyoStructure[] { 154 | const publicKeys: XyoStructure[] = [] 155 | 156 | for (const signer of this.signers) { 157 | publicKeys.push(signer.getPublicKey()) 158 | } 159 | 160 | return publicKeys 161 | } 162 | 163 | private signBoundWitness(_payload: XyoStructure[]) { 164 | const signatures: XyoStructure[] = [] 165 | 166 | for (const signer of this.signers) { 167 | signatures.push(this.sign(signer)) 168 | } 169 | 170 | const witness = XyoBoundWitness.createMasterArrayWithSubArray( 171 | XyoObjectSchema.WITNESS, 172 | XyoObjectSchema.SIGNATURE_SET, 173 | this.unsignedPayload, 174 | signatures 175 | ) 176 | 177 | this.addToLedger(witness) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/hashing/index.ts: -------------------------------------------------------------------------------- 1 | import XyoHasher from './xyo-hasher' 2 | export { XyoSha256 } from './xyo-sha256' 3 | 4 | export { XyoHasher } 5 | -------------------------------------------------------------------------------- /src/hashing/xyo-hasher.ts: -------------------------------------------------------------------------------- 1 | import { XyoStructure } from '../object-model' 2 | 3 | abstract class XyoHasher { 4 | abstract hash(data: Buffer): XyoStructure 5 | } 6 | 7 | export default XyoHasher 8 | -------------------------------------------------------------------------------- /src/hashing/xyo-sha256.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | 3 | import { XyoBuffer, XyoStructure } from '../object-model' 4 | import { XyoObjectSchema } from '../schema' 5 | import XyoHasher from './xyo-hasher' 6 | 7 | export class XyoSha256 implements XyoHasher { 8 | public hash(data: Buffer): XyoStructure { 9 | const rawHash = crypto.createHash('sha256').update(data).digest() 10 | const buffer = new XyoBuffer(rawHash) 11 | return XyoStructure.newInstance(XyoObjectSchema.SHA_256, buffer) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/heuristics/common/defaultResolvers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: defaultResolvers.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 2:22:50 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 2:30:21 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | import { XyoObjectSchema } from '../../schema' 14 | import { 15 | boundWitnessResolver, 16 | bridgeHashSetResolver, 17 | fetterResolver, 18 | gpsResolver, 19 | indexResolver, 20 | keySetResolver, 21 | latResolver, 22 | lngResolver, 23 | paymentKeyResolver, 24 | previousHashResolver, 25 | rsaPublicKeyResolver, 26 | rsaSignatureResolver, 27 | rssiAt1MResolver, 28 | rssiResolver, 29 | secp256K1PublicKeyResolver, 30 | secp256K1SignatureResolver, 31 | sha3Resolver, 32 | sha256Resolver, 33 | signatureSetResolver, 34 | stubHashResolver, 35 | stubPublicKey, 36 | stubSignatureResolver, 37 | timeResolver, 38 | witnessResolver, 39 | } from './resolvers' 40 | 41 | const defaultResolvers = [ 42 | { 43 | id: XyoObjectSchema.BLE_POWER_LEVEL.id, 44 | resolver: rssiAt1MResolver, 45 | }, 46 | { 47 | id: XyoObjectSchema.EC_PUBLIC_KEY.id, 48 | resolver: secp256K1PublicKeyResolver, 49 | }, 50 | { 51 | id: XyoObjectSchema.EC_SIGNATURE.id, 52 | resolver: secp256K1SignatureResolver, 53 | }, 54 | { 55 | id: XyoObjectSchema.INDEX.id, 56 | resolver: indexResolver, 57 | }, 58 | { 59 | id: XyoObjectSchema.LAT.id, 60 | resolver: latResolver, 61 | }, 62 | { 63 | id: XyoObjectSchema.LNG.id, 64 | resolver: lngResolver, 65 | }, 66 | { 67 | id: XyoObjectSchema.PAYMENT_KEY.id, 68 | resolver: paymentKeyResolver, 69 | }, 70 | { 71 | id: XyoObjectSchema.RSA_PUBLIC_KEY.id, 72 | resolver: rsaPublicKeyResolver, 73 | }, 74 | { 75 | id: XyoObjectSchema.RSA_SIGNATURE.id, 76 | resolver: rsaSignatureResolver, 77 | }, 78 | { 79 | id: XyoObjectSchema.RSSI.id, 80 | resolver: rssiResolver, 81 | }, 82 | { 83 | id: XyoObjectSchema.SHA_256.id, 84 | resolver: sha256Resolver, 85 | }, 86 | { 87 | id: XyoObjectSchema.SHA_3.id, 88 | resolver: sha3Resolver, 89 | }, 90 | { 91 | id: XyoObjectSchema.STUB_HASH.id, 92 | resolver: stubHashResolver, 93 | }, 94 | { 95 | id: XyoObjectSchema.STUB_PUBLIC_KEY.id, 96 | resolver: stubPublicKey, 97 | }, 98 | { 99 | id: XyoObjectSchema.STUB_SIGNATURE.id, 100 | resolver: stubSignatureResolver, 101 | }, 102 | { 103 | id: XyoObjectSchema.UNIX_TIME.id, 104 | resolver: timeResolver, 105 | }, 106 | { 107 | id: XyoObjectSchema.GPS.id, 108 | resolver: gpsResolver, 109 | }, 110 | { 111 | id: XyoObjectSchema.BW.id, 112 | resolver: boundWitnessResolver, 113 | }, 114 | { 115 | id: XyoObjectSchema.KEY_SET.id, 116 | resolver: keySetResolver, 117 | }, 118 | { 119 | id: XyoObjectSchema.SIGNATURE_SET.id, 120 | resolver: signatureSetResolver, 121 | }, 122 | { 123 | id: XyoObjectSchema.FETTER.id, 124 | resolver: fetterResolver, 125 | }, 126 | { 127 | id: XyoObjectSchema.WITNESS.id, 128 | resolver: witnessResolver, 129 | }, 130 | { 131 | id: XyoObjectSchema.PREVIOUS_HASH.id, 132 | resolver: previousHashResolver, 133 | }, 134 | { 135 | id: XyoObjectSchema.BRIDGE_HASH_SET.id, 136 | resolver: bridgeHashSetResolver, 137 | }, 138 | ] 139 | 140 | export default defaultResolvers 141 | -------------------------------------------------------------------------------- /src/heuristics/common/index.ts: -------------------------------------------------------------------------------- 1 | import { XyoHumanHeuristicResolver } from '../xyo-heuristic-resolver' 2 | import defaultResolvers from './defaultResolvers' 3 | export * from './resolvers' 4 | 5 | export const addAllDefaults = () => { 6 | XyoHumanHeuristicResolver.addResolvers(defaultResolvers) 7 | } 8 | -------------------------------------------------------------------------------- /src/heuristics/common/resolvers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: index copy.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 2:27:23 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 2:31:15 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | import bs58 from 'bs58' 14 | 15 | import { XyoIterableStructure, XyoStructure } from '../../object-model' 16 | import { 17 | IXyoHeuristicResolver, 18 | IXyoHumanHeuristic, 19 | XyoHumanHeuristicResolver, 20 | } from '../xyo-heuristic-resolver' 21 | import { 22 | iterableObjectToArray, 23 | readSignedNumber, 24 | readUnsignedNumber, 25 | uniqueIterableToObject, 26 | } from './xyo-heuristics-common-util' 27 | 28 | export const nextPublicKeyResolver: IXyoHeuristicResolver = { 29 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 30 | const key = new XyoIterableStructure(heuristic).get(0) 31 | 32 | return { 33 | name: 'nextPublicKey', 34 | value: XyoHumanHeuristicResolver.resolve(key.getAll().getContentsCopy()), 35 | } 36 | }, 37 | } 38 | 39 | export const paymentKeyResolver: IXyoHeuristicResolver = { 40 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 41 | //const key = new XyoIterableStructure(heuristic).get(0) 42 | 43 | return { 44 | name: 'paymentKey', 45 | value: heuristic.toString('base64'), 46 | } 47 | }, 48 | } 49 | 50 | export const secp256K1SignatureResolver: IXyoHeuristicResolver = { 51 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 52 | return { 53 | name: 'secp566k1Signature', 54 | value: heuristic.toString('base64'), 55 | } 56 | }, 57 | } 58 | 59 | export const rsaSignatureResolver: IXyoHeuristicResolver = { 60 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 61 | return { 62 | name: 'rsaSignature', 63 | value: heuristic.toString('base64'), 64 | } 65 | }, 66 | } 67 | 68 | export const secp256K1PublicKeyResolver: IXyoHeuristicResolver = { 69 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 70 | return { 71 | name: 'secp566k1PublicKey', 72 | value: bs58.encode(heuristic), 73 | } 74 | }, 75 | } 76 | 77 | export const indexResolver: IXyoHeuristicResolver = { 78 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 79 | return { 80 | name: 'index', 81 | value: readUnsignedNumber( 82 | new XyoStructure(heuristic).getValue().getContentsCopy() 83 | ), 84 | } 85 | }, 86 | } 87 | 88 | export const rssiResolver: IXyoHeuristicResolver = { 89 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 90 | return { 91 | name: 'rssi', 92 | value: readSignedNumber( 93 | new XyoStructure(heuristic).getValue().getContentsCopy() 94 | ), 95 | } 96 | }, 97 | } 98 | 99 | export const rssiAt1MResolver: IXyoHeuristicResolver = { 100 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 101 | return { 102 | name: 'rssiAt1m', 103 | value: readSignedNumber( 104 | new XyoStructure(heuristic).getValue().getContentsCopy() 105 | ), 106 | } 107 | }, 108 | } 109 | 110 | export const stubSignatureResolver: IXyoHeuristicResolver = { 111 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 112 | return { 113 | name: 'stubSignature', 114 | value: heuristic.toString('base64'), 115 | } 116 | }, 117 | } 118 | 119 | export const rsaPublicKeyResolver: IXyoHeuristicResolver = { 120 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 121 | return { 122 | name: 'rsaPublicKey', 123 | value: bs58.encode(heuristic), 124 | } 125 | }, 126 | } 127 | 128 | export const stubPublicKey: IXyoHeuristicResolver = { 129 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 130 | return { 131 | name: 'stubPublicKey', 132 | value: bs58.encode(heuristic), 133 | } 134 | }, 135 | } 136 | 137 | export const stubHashResolver: IXyoHeuristicResolver = { 138 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 139 | return { 140 | name: 'stubHash', 141 | value: bs58.encode(heuristic), 142 | } 143 | }, 144 | } 145 | 146 | export const sha256Resolver: IXyoHeuristicResolver = { 147 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 148 | return { 149 | name: 'sha256', 150 | value: bs58.encode(heuristic), 151 | } 152 | }, 153 | } 154 | 155 | export const sha3Resolver: IXyoHeuristicResolver = { 156 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 157 | return { 158 | name: 'sha3', 159 | value: bs58.encode(heuristic), 160 | } 161 | }, 162 | } 163 | 164 | export const latResolver: IXyoHeuristicResolver = { 165 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 166 | const value = new XyoStructure(heuristic).getValue().getContentsCopy() 167 | return { 168 | name: 'lat', 169 | value: value.readDoubleBE(0), 170 | } 171 | }, 172 | } 173 | 174 | export const lngResolver: IXyoHeuristicResolver = { 175 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 176 | const value = new XyoStructure(heuristic).getValue().getContentsCopy() 177 | return { 178 | name: 'lng', 179 | value: value.readDoubleBE(0), 180 | } 181 | }, 182 | } 183 | 184 | export const timeResolver: IXyoHeuristicResolver = { 185 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 186 | const value = new XyoStructure(heuristic).getValue().getContentsCopy() 187 | return { 188 | name: 'date', 189 | value: value.readUIntBE(2, 6), 190 | } 191 | }, 192 | } 193 | 194 | export const gpsResolver: IXyoHeuristicResolver = { 195 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 196 | return { 197 | name: 'gps', 198 | value: uniqueIterableToObject(new XyoIterableStructure(heuristic)), 199 | } 200 | }, 201 | } 202 | 203 | export const fetterResolver: IXyoHeuristicResolver = { 204 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 205 | return { 206 | name: 'fetter', 207 | value: uniqueIterableToObject(new XyoIterableStructure(heuristic)), 208 | } 209 | }, 210 | } 211 | 212 | export const witnessResolver: IXyoHeuristicResolver = { 213 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 214 | return { 215 | name: 'witness', 216 | value: uniqueIterableToObject(new XyoIterableStructure(heuristic)), 217 | } 218 | }, 219 | } 220 | 221 | export const keySetResolver: IXyoHeuristicResolver = { 222 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 223 | return { 224 | name: 'keySet', 225 | value: iterableObjectToArray(new XyoIterableStructure(heuristic)), 226 | } 227 | }, 228 | } 229 | 230 | export const signatureSetResolver: IXyoHeuristicResolver = { 231 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 232 | return { 233 | name: 'signatureSet', 234 | value: iterableObjectToArray(new XyoIterableStructure(heuristic)), 235 | } 236 | }, 237 | } 238 | 239 | export const previousHashResolver: IXyoHeuristicResolver = { 240 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 241 | const children = iterableObjectToArray(new XyoIterableStructure(heuristic)) 242 | 243 | if (children.length !== 1) { 244 | // we expect the previous hash to be a length of 1 245 | return { 246 | name: 'previousHash', 247 | value: 'invalid', 248 | } 249 | } 250 | 251 | return { 252 | name: 'previousHash', 253 | value: children[0].value, 254 | } 255 | }, 256 | } 257 | 258 | export const boundWitnessResolver: IXyoHeuristicResolver = { 259 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 260 | return { 261 | name: 'boundWitness', 262 | value: iterableObjectToArray(new XyoIterableStructure(heuristic)).map( 263 | (h) => { 264 | return h.value 265 | } 266 | ), 267 | } 268 | }, 269 | } 270 | 271 | export const bridgeHashSetResolver: IXyoHeuristicResolver = { 272 | resolve(heuristic: Buffer): IXyoHumanHeuristic { 273 | return { 274 | name: 'bridgeHashSet', 275 | value: iterableObjectToArray(new XyoIterableStructure(heuristic)).map( 276 | (h) => { 277 | return h.value 278 | } 279 | ), 280 | } 281 | }, 282 | } 283 | -------------------------------------------------------------------------------- /src/heuristics/common/xyo-heuristics-common-util.ts: -------------------------------------------------------------------------------- 1 | import { XyoIterableStructure } from '../../object-model' 2 | import { 3 | IXyoHumanHeuristic, 4 | XyoHumanHeuristicResolver, 5 | } from '../xyo-heuristic-resolver' 6 | 7 | export const readSignedNumber = (buffer: Buffer): number => { 8 | switch (buffer.length) { 9 | case 1: 10 | return buffer.readInt8(0) 11 | case 2: 12 | return buffer.readInt16BE(0) 13 | case 4: 14 | return buffer.readInt32BE(0) 15 | } 16 | 17 | return -1 18 | } 19 | 20 | export const readUnsignedNumber = (buffer: Buffer): number => { 21 | switch (buffer.length) { 22 | case 1: 23 | return buffer.readUInt8(0) 24 | case 2: 25 | return buffer.readUInt16BE(0) 26 | case 4: 27 | return buffer.readUInt32BE(0) 28 | } 29 | 30 | return -1 31 | } 32 | 33 | export const uniqueIterableToObject = ( 34 | buffer: XyoIterableStructure 35 | ): { [key: string]: any } => { 36 | const it = buffer.newIterator() 37 | const map: { [key: string]: any } = {} 38 | 39 | while (it.hasNext()) { 40 | const child = it.next().value 41 | const readable = XyoHumanHeuristicResolver.resolve( 42 | child.getAll().getContentsCopy() 43 | ) 44 | map[readable.name] = readable.value 45 | } 46 | 47 | return map 48 | } 49 | 50 | export const iterableObjectToArray = ( 51 | buffer: XyoIterableStructure 52 | ): IXyoHumanHeuristic[] => { 53 | const it = buffer.newIterator() 54 | const arr: IXyoHumanHeuristic[] = [] 55 | 56 | while (it.hasNext()) { 57 | const child = it.next().value 58 | const readable = XyoHumanHeuristicResolver.resolve( 59 | child.getAll().getContentsCopy() 60 | ) 61 | arr.push(readable) 62 | } 63 | 64 | return arr 65 | } 66 | -------------------------------------------------------------------------------- /src/heuristics/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common' 2 | export type { IXyoBoundWitnessOrigin } from './xyo-bound-witness-origin' 3 | export { XyoBoundWitnessOriginGetter } from './xyo-bound-witness-origin' 4 | export type { IXyoHeuristicGetter } from './xyo-heuristic-getter' 5 | export * from './xyo-heuristic-resolver' 6 | export type { IXyoBoundWitnessPayload } from './xyo-payload' 7 | -------------------------------------------------------------------------------- /src/heuristics/spec/xyo-heuristic-resolver.spec.ts: -------------------------------------------------------------------------------- 1 | import { addAllDefaults } from '../common' 2 | 3 | describe('XyoHueristicResolver', () => { 4 | it('Resolve bound witness', () => { 5 | /*const bytes = Buffer.from( 6 | 'YAIB52AVAKswGUQADEFQ4NYrw6iR4rQTxzE2pkjDMU1AaBPSzVwGwsOYzR4dfNi4/377pNVy6H+KUVYvVSkA7l0kCUlagSJpD++h3dxLAB8FAAAAAAAgAgAwCCQADyHgrxrt3hyS9Ci61BxkcT4pRNBuNB0d9r4gPhFd93fqtQATAsYAAwUAAD43cAYAJQAQIeCvGu3eHJL0KLrUHGRxPilE0G40HR32viA+EV33d+q1IBWgIBlEAAxBWDofWDa0i+X1fdUYlW8j6kPKbRe5vkcqvnMz0EydYGw/iMTZFKoFkaJYCSSRV6HdDEToZUFt8u955mKa/xwjfCASFwAcCUBBCv3p/BF0AB0JwF1wa99UHiUAFAkAAAFqanx5mgAeAgEAEwLLMAgkABAh4K8a7d4ckvQoutQcZHE+KUTQbjQdHfa+ID4RXfd36rUAAwUAAH1VIBdJIBpGAAlDIDK4p8dtbdtxwie93wUn7/kMPZ+5WnDy/T2VXbNQPk1eIFB0rb4JGvrXNA/m29UlpjsVPy+NZJf261Zb7DVOvYxFIBdJMBpGAAlDIL06EAmSLev6tCuibLWIm7OhC01E6t2N3OdHxrK9Oya+IKfYtFqptP3puzQxy5zgy7hSprafJd38Pun5GfaGM5PC', 7 | 'base64' 8 | )*/ 9 | addAllDefaults() 10 | // console.log(JSON.stringify(XyoHumanHeuristicResolver.resolve(bytes))) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /src/heuristics/xyo-bound-witness-origin.ts: -------------------------------------------------------------------------------- 1 | import { XyoBoundWitness } from '../bound-witness' 2 | import { XyoIterableStructure, XyoStructure } from '../object-model' 3 | import { XyoObjectSchema } from '../schema' 4 | import { indexResolver } from './common' 5 | 6 | export interface IXyoBoundWitnessOrigin { 7 | index: number 8 | nextPublicKey: Buffer | undefined 9 | previousHash: Buffer | undefined 10 | } 11 | 12 | export class XyoBoundWitnessOriginGetter { 13 | public static getOriginInformation( 14 | boundWitness: XyoBoundWitness 15 | ): IXyoBoundWitnessOrigin[] { 16 | const origins: IXyoBoundWitnessOrigin[] = [] 17 | const allHeuristics = boundWitness.getHeuristics() 18 | 19 | for (const fetterHeuristics of allHeuristics) { 20 | origins.push( 21 | XyoBoundWitnessOriginGetter.getOriginInformationFromFetter( 22 | fetterHeuristics 23 | ) 24 | ) 25 | } 26 | 27 | return origins 28 | } 29 | 30 | private static getOriginInformationFromFetter( 31 | fetterHeuristics: XyoStructure[] 32 | ): IXyoBoundWitnessOrigin { 33 | let nextPublicKey: Buffer | undefined 34 | let previousHash: Buffer | undefined 35 | let index = -1 36 | 37 | for (const heuristic of fetterHeuristics) { 38 | if (heuristic.getSchema().id === XyoObjectSchema.NEXT_PUBLIC_KEY.id) { 39 | const nextPublicKeyArray = heuristic as XyoIterableStructure 40 | 41 | if (nextPublicKeyArray.getCount() !== 1) { 42 | throw new Error('1 next public key expected') 43 | } 44 | 45 | nextPublicKey = nextPublicKeyArray.get(0).getAll().getContentsCopy() 46 | } 47 | 48 | if (heuristic.getSchema().id === XyoObjectSchema.PREVIOUS_HASH.id) { 49 | const previousHashArray = heuristic as XyoIterableStructure 50 | 51 | if (previousHashArray.getCount() !== 1) { 52 | throw new Error('1 hash expected in previous hash') 53 | } 54 | 55 | previousHash = previousHashArray.get(0).getAll().getContentsCopy() 56 | } 57 | 58 | if (heuristic.getSchema().id === XyoObjectSchema.INDEX.id) { 59 | index = indexResolver.resolve(heuristic.getAll().getContentsCopy()) 60 | .value 61 | } 62 | } 63 | 64 | return { 65 | index, 66 | nextPublicKey, 67 | previousHash, 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/heuristics/xyo-heuristic-getter.ts: -------------------------------------------------------------------------------- 1 | import { XyoStructure } from '../object-model' 2 | 3 | export interface IXyoHeuristicGetter { 4 | getHeuristic(choice?: Buffer): XyoStructure | undefined 5 | } 6 | -------------------------------------------------------------------------------- /src/heuristics/xyo-heuristic-resolver.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer, XyoStructure } from '../object-model' 2 | 3 | export interface IXyoHeuristicResolver { 4 | resolve(heuristic: Buffer): IXyoHumanHeuristic 5 | } 6 | 7 | export interface IXyoHumanHeuristic { 8 | name: string 9 | value: any 10 | } 11 | 12 | export class XyoHumanHeuristicResolver { 13 | public static addResolvers( 14 | resolvers: { id: number; resolver: IXyoHeuristicResolver }[] 15 | ) { 16 | for (const resolver of resolvers) { 17 | this.addResolver(resolver.id, resolver.resolver) 18 | } 19 | } 20 | 21 | public static addResolver(forId: number, resolver: IXyoHeuristicResolver) { 22 | XyoHumanHeuristicResolver.resolvers.set(forId, resolver) 23 | } 24 | 25 | public static removeResolver(forId: number) { 26 | XyoHumanHeuristicResolver.resolvers.delete(forId) 27 | } 28 | 29 | public static resolve(any: Buffer): IXyoHumanHeuristic { 30 | const item = new XyoStructure(new XyoBuffer(any)) 31 | 32 | const resolver = XyoHumanHeuristicResolver.resolvers.get( 33 | item.getSchema().id 34 | ) 35 | 36 | if (resolver) { 37 | return resolver.resolve(item.getAll().getContentsCopy()) 38 | } 39 | 40 | return { 41 | name: item.getSchema().id.toString(), 42 | value: any.toString('base64'), 43 | } 44 | } 45 | 46 | private static resolvers: Map = new Map() 47 | } 48 | -------------------------------------------------------------------------------- /src/heuristics/xyo-payload.ts: -------------------------------------------------------------------------------- 1 | import { XyoStructure } from '../object-model' 2 | 3 | export interface IXyoBoundWitnessPayload { 4 | signed: XyoStructure[] 5 | unsigned: XyoStructure[] 6 | } 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bound-witness' 2 | export * from './hashing' 3 | export * from './heuristics' 4 | export * from './network' 5 | export * from './object-model' 6 | export * from './origin' 7 | export * from './persist' 8 | export * from './schema' 9 | export * from './signing' 10 | export * from './simulation' 11 | -------------------------------------------------------------------------------- /src/network/index.ts: -------------------------------------------------------------------------------- 1 | import XyoNetworkPipe from './xyo-network-pipe' 2 | import XyoProcedureCatalog from './xyo-procedure-catalog' 3 | 4 | export * from './tcp' 5 | export { XyoAdvertisePacket } from './xyo-advertise-packet' 6 | export { XyoCatalogFlags } from './xyo-catalog-flags' 7 | export { XyoChoicePacket } from './xyo-choice-packet' 8 | export { XyoNetworkHandler } from './xyo-network-handler' 9 | 10 | export { XyoNetworkPipe, XyoProcedureCatalog } 11 | -------------------------------------------------------------------------------- /src/network/tcp/index.ts: -------------------------------------------------------------------------------- 1 | export { XyoTcpPipe } from './xyo-tcp-pipe' 2 | export { XyoServerTcpNetwork } from './xyo-tcp-server-network' 3 | -------------------------------------------------------------------------------- /src/network/tcp/xyo-tcp-pipe.ts: -------------------------------------------------------------------------------- 1 | import { XyoBase } from '@xyo-network/sdk-base-nodejs' 2 | import net from 'net' 3 | 4 | import { XyoAdvertisePacket } from '../xyo-advertise-packet' 5 | import XyoNetworkPipe from '../xyo-network-pipe' 6 | 7 | export class XyoTcpPipe extends XyoBase implements XyoNetworkPipe { 8 | private socket: net.Socket 9 | private initData: XyoAdvertisePacket | undefined 10 | 11 | constructor(socket: net.Socket, initData: XyoAdvertisePacket | undefined) { 12 | super() 13 | this.socket = socket 14 | this.initData = initData 15 | } 16 | 17 | public getInitiationData(): XyoAdvertisePacket | undefined { 18 | return this.initData 19 | } 20 | 21 | public async send( 22 | data: Buffer, 23 | waitForResponse: boolean 24 | ): Promise { 25 | this.logVerbose(`Sending data through socket: ${data.toString('hex')}`) 26 | await this.sendData(data) 27 | 28 | if (waitForResponse) { 29 | const response = await this.waitForMessage() 30 | this.logVerbose(`Got data through socket: ${response.toString('hex')}`) 31 | return response 32 | } 33 | 34 | return undefined 35 | } 36 | 37 | // eslint-disable-next-line require-await 38 | public async close() { 39 | this.logInfo( 40 | `Closing connection with ${this.socket.remoteAddress}:${this.socket.remotePort}` 41 | ) 42 | this.socket.end() 43 | 44 | setTimeout(() => { 45 | if (!this.socket.destroyed) { 46 | this.socket.destroy() 47 | } 48 | }, 2_000) 49 | } 50 | 51 | private waitForMessage(): Promise { 52 | return new Promise((resolve, reject) => { 53 | let hasResumed = false 54 | let waitSize: number 55 | let currentSize = 0 56 | let currentBuffer = Buffer.alloc(0) 57 | 58 | const cleanup = () => { 59 | this.socket.removeAllListeners('data') 60 | this.socket.removeAllListeners('close') 61 | this.socket.removeAllListeners('end') 62 | } 63 | 64 | const onTimeout = () => { 65 | if (!hasResumed) { 66 | hasResumed = true 67 | this.socket.destroy() 68 | cleanup() 69 | reject(new Error('timeout')) 70 | } 71 | } 72 | 73 | const onClose = () => { 74 | if (!hasResumed) { 75 | hasResumed = true 76 | cleanup() 77 | reject(new Error('Socket closed while waiting for write')) 78 | } 79 | } 80 | 81 | this.socket.on('data', (data: Buffer) => { 82 | currentSize += data.length 83 | currentBuffer = Buffer.concat([currentBuffer, data]) 84 | 85 | if (waitSize === undefined && currentSize >= 4) { 86 | waitSize = currentBuffer.readUInt32BE(0) 87 | } 88 | 89 | if (currentSize >= waitSize && !hasResumed) { 90 | hasResumed = true 91 | cleanup() 92 | resolve(currentBuffer.slice(4)) 93 | } 94 | }) 95 | 96 | this.socket.on('close', onClose) 97 | this.socket.on('error', onClose) 98 | 99 | setTimeout(onTimeout, 7_500) 100 | }) 101 | } 102 | 103 | private sendData(data: Buffer): Promise { 104 | return new Promise((resolve, reject) => { 105 | const sizeBuffer = Buffer.alloc(4) 106 | sizeBuffer.writeUInt32BE(data.length + 4, 0) 107 | const dataWithSize = Buffer.concat([sizeBuffer, data]) 108 | 109 | this.socket.write(dataWithSize, (error) => { 110 | if (error === undefined) { 111 | resolve() 112 | } else { 113 | reject(error) 114 | } 115 | }) 116 | }) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/network/tcp/xyo-tcp-server-network.ts: -------------------------------------------------------------------------------- 1 | import { XyoBase } from '@xyo-network/sdk-base-nodejs' 2 | import net from 'net' 3 | 4 | import { XyoAdvertisePacket } from '../xyo-advertise-packet' 5 | import { XyoTcpPipe } from './xyo-tcp-pipe' 6 | 7 | export class XyoServerTcpNetwork extends XyoBase { 8 | public onPipeCreated: ((pipe: XyoTcpPipe) => boolean) | undefined 9 | public server: net.Server 10 | public port: number 11 | 12 | constructor(port: number) { 13 | super() 14 | this.port = port 15 | this.server = net.createServer(this.connectionListener.bind(this)) 16 | 17 | this.server.on('error', (e) => { 18 | this.logWarning(`Unknown server socket error: ${e}`) 19 | 20 | if (!this.server.listening) { 21 | this.logInfo(`Starting listing again on port ${this.port}`) 22 | this.server.listen(this.port) 23 | } 24 | }) 25 | } 26 | 27 | public startListening() { 28 | this.server.listen(this.port) 29 | } 30 | 31 | public stopListening() { 32 | this.server.close() 33 | } 34 | 35 | private connectionListener(socket: net.Socket) { 36 | this.logInfo( 37 | `New connection made with ${socket.remoteAddress}:${socket.remotePort}` 38 | ) 39 | 40 | socket.on('error', (e) => { 41 | this.logWarning(`Unknown socket error: ${e}`) 42 | socket.destroy() 43 | }) 44 | 45 | let waitSize: number 46 | let currentSize = 0 47 | let currentBuffer = Buffer.alloc(0) 48 | 49 | const cleanup = () => { 50 | socket.removeAllListeners('data') 51 | socket.removeAllListeners('close') 52 | socket.removeAllListeners('end') 53 | socket.removeAllListeners('timeout') 54 | socket.destroy() 55 | } 56 | 57 | const timeout = setTimeout(cleanup, 5_000) 58 | 59 | socket.on('timeout', () => { 60 | cleanup() 61 | }) 62 | 63 | socket.on('data', (data: Buffer) => { 64 | currentSize += data.length 65 | currentBuffer = Buffer.concat([currentBuffer, data]) 66 | 67 | if (waitSize === undefined && currentSize >= 4) { 68 | waitSize = currentBuffer.readUInt32BE(0) 69 | } 70 | 71 | if (currentSize >= waitSize) { 72 | clearTimeout(timeout) 73 | 74 | if (!this.onInternalPipeCreated(socket, currentBuffer.slice(4))) { 75 | cleanup() 76 | } 77 | 78 | socket.removeAllListeners('data') 79 | socket.removeAllListeners('close') 80 | socket.removeAllListeners('end') 81 | } 82 | }) 83 | 84 | socket.on('close', cleanup) 85 | socket.on('end', cleanup) 86 | 87 | socket.setTimeout(1000 * 60 * 1) // 1 min 88 | } 89 | 90 | private onInternalPipeCreated(socket: net.Socket, data: Buffer): boolean { 91 | const socketPipe = new XyoTcpPipe(socket, new XyoAdvertisePacket(data)) 92 | const callback = this.onPipeCreated 93 | 94 | if (callback) { 95 | return callback(socketPipe) 96 | } 97 | 98 | return false 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/network/xyo-advertise-packet.ts: -------------------------------------------------------------------------------- 1 | export class XyoAdvertisePacket { 2 | private data: Buffer 3 | 4 | constructor(data: Buffer) { 5 | this.data = data 6 | } 7 | 8 | public getChoice() { 9 | if (this.data.length === 0) { 10 | throw new Error('getChoice: Out of index, can`t be 0') 11 | } 12 | 13 | const sizeOfChoice = this.data.readUInt8(0) 14 | 15 | if (sizeOfChoice + 1 > this.data.length) { 16 | throw new Error('getChoice: Out of index!') 17 | } 18 | 19 | return this.data.slice(1, sizeOfChoice + 1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/network/xyo-catalog-flags.ts: -------------------------------------------------------------------------------- 1 | export class XyoCatalogFlags { 2 | public static BOUND_WITNESS = 1 3 | public static TAKE_ORIGIN_CHAIN = 2 4 | public static GIVE_ORIGIN_CHAIN = 4 5 | 6 | public static flip(flags: Buffer): Buffer { 7 | if (flags.length === 0) { 8 | return flags 9 | } 10 | 11 | if ((flags[0] & XyoCatalogFlags.TAKE_ORIGIN_CHAIN) !== 0) { 12 | return new Buffer([XyoCatalogFlags.GIVE_ORIGIN_CHAIN]) 13 | } 14 | 15 | if ((flags[0] & XyoCatalogFlags.GIVE_ORIGIN_CHAIN) !== 0) { 16 | return new Buffer([XyoCatalogFlags.TAKE_ORIGIN_CHAIN]) 17 | } 18 | 19 | return new Buffer([XyoCatalogFlags.BOUND_WITNESS]) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/network/xyo-choice-packet.ts: -------------------------------------------------------------------------------- 1 | export class XyoChoicePacket { 2 | private data: Buffer 3 | 4 | constructor(data: Buffer) { 5 | this.data = data 6 | } 7 | 8 | public getChoice(): Buffer { 9 | const sizeOfChoice = this.getSizeOfChoice() 10 | 11 | if (sizeOfChoice + 1 > this.data.length) { 12 | throw new Error('getChoice: Out of index') 13 | } 14 | 15 | return this.data.slice(1, sizeOfChoice + 1) 16 | } 17 | 18 | public getResponse(): Buffer { 19 | const sizeOfChoice = this.getSizeOfChoice() 20 | 21 | if (sizeOfChoice + 1 > this.data.length) { 22 | throw new Error('getResponse: Out of index') 23 | } 24 | 25 | return this.data.slice(1 + sizeOfChoice) 26 | } 27 | 28 | private getSizeOfChoice(): number { 29 | if (this.data.length === 0) { 30 | throw new Error('getSizeOfChoice: Out of index') 31 | } 32 | 33 | return this.data.readUInt8(0) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/network/xyo-network-handler.ts: -------------------------------------------------------------------------------- 1 | import XyoNetworkPipe from './xyo-network-pipe' 2 | 3 | export class XyoNetworkHandler { 4 | private static getSizeEncodedCatalog(catalog: Buffer): Buffer { 5 | const sizeBuffer = Buffer.alloc(1) 6 | sizeBuffer.writeUInt8(catalog.length, 0) 7 | return Buffer.concat([sizeBuffer, catalog]) 8 | } 9 | 10 | public pipe: XyoNetworkPipe 11 | 12 | constructor(pipe: XyoNetworkPipe) { 13 | this.pipe = pipe 14 | } 15 | 16 | public sendCatalogPacket(catalog: Buffer): Promise { 17 | const buffer = XyoNetworkHandler.getSizeEncodedCatalog(catalog) 18 | return this.pipe.send(buffer, true) 19 | } 20 | 21 | public sendChoicePacket( 22 | choice: Buffer, 23 | response: Buffer 24 | ): Promise { 25 | const bufferToSend = Buffer.concat([ 26 | XyoNetworkHandler.getSizeEncodedCatalog(choice), 27 | response, 28 | ]) 29 | 30 | return this.pipe.send(bufferToSend, true) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/network/xyo-network-pipe.ts: -------------------------------------------------------------------------------- 1 | import { XyoAdvertisePacket } from './xyo-advertise-packet' 2 | 3 | abstract class XyoNetworkPipe { 4 | abstract getInitiationData(): XyoAdvertisePacket | undefined 5 | abstract send( 6 | data: Buffer, 7 | waitForResponse: boolean 8 | ): Promise 9 | abstract close(): Promise 10 | } 11 | 12 | export default XyoNetworkPipe 13 | -------------------------------------------------------------------------------- /src/network/xyo-procedure-catalog.ts: -------------------------------------------------------------------------------- 1 | abstract class XyoProcedureCatalog { 2 | abstract getEncodedCanDo(): Buffer 3 | abstract canDo(otherCatalog: Buffer): boolean 4 | abstract choose(catalog: Buffer): Buffer 5 | } 6 | 7 | export default XyoProcedureCatalog 8 | -------------------------------------------------------------------------------- /src/object-model/index.ts: -------------------------------------------------------------------------------- 1 | export { XyoBuffer } from './xyo-buffer' 2 | export { XyoIterableStructure } from './xyo-iterable-structure' 3 | export { XyoIterator } from './xyo-iterator' 4 | export { XyoSchema } from './xyo-schema' 5 | export { XyoSize } from './xyo-size' 6 | export { XyoStructure } from './xyo-structure' 7 | -------------------------------------------------------------------------------- /src/object-model/size-util.ts: -------------------------------------------------------------------------------- 1 | import { XyoSize } from './xyo-size' 2 | 3 | export class XyoSizeUtil { 4 | public static getBestSize(size: number): XyoSize { 5 | if (size + 1 < 2 ** 8) { 6 | return XyoSize.ONE 7 | } 8 | 9 | if (size + 2 < 2 ** 16) { 10 | return XyoSize.TWO 11 | } 12 | 13 | if (size + 4 < 2 ** 32) { 14 | return XyoSize.FOUR 15 | } 16 | 17 | return XyoSize.EIGHT 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/object-model/spec/xyo-buffer-test.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer } from '../xyo-buffer' 2 | 3 | describe('XyoBuffer', () => { 4 | it('Same as input', () => { 5 | const inputBuffer = Buffer.from('1337') 6 | const buffer = new XyoBuffer(inputBuffer) 7 | const outputBuffer = buffer.getContentsCopy() 8 | 9 | expect(outputBuffer).toBe(outputBuffer) 10 | }) 11 | 12 | it('1 as size', () => { 13 | const inputBuffer = Buffer.from('1337', 'hex') 14 | const expected = Buffer.from('37', 'hex') 15 | const buffer = new XyoBuffer(inputBuffer, 1, 2) 16 | const outputBuffer = buffer.getContentsCopy() 17 | 18 | expect(outputBuffer.values).toBe(expected.values) 19 | }) 20 | 21 | it('Cut start', () => { 22 | const inputBuffer = Buffer.from('13370001', 'hex') 23 | const expected = Buffer.from('0001', 'hex') 24 | const buffer = new XyoBuffer(inputBuffer, 2, 4) 25 | const outputBuffer = buffer.getContentsCopy() 26 | 27 | expect(outputBuffer.values).toBe(expected.values) 28 | }) 29 | 30 | it('Cut end', () => { 31 | const inputBuffer = Buffer.from('13370001', 'hex') 32 | const expected = Buffer.from('1337', 'hex') 33 | const buffer = new XyoBuffer(inputBuffer, 0, 2) 34 | const outputBuffer = buffer.getContentsCopy() 35 | 36 | expect(outputBuffer.values).toBe(expected.values) 37 | }) 38 | 39 | it('Get int 8', () => { 40 | const inputBuffer = Buffer.from('00ff00', 'hex') 41 | const buffer = new XyoBuffer(inputBuffer) 42 | 43 | expect(buffer.getUInt8(1)).toBe(0xff) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /src/object-model/spec/xyo-iterable-structure.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer } from '../xyo-buffer' 2 | import { XyoIterableStructure } from '../xyo-iterable-structure' 3 | 4 | describe('XyoBuffer', () => { 5 | it('Untyped 1', () => { 6 | const inputBuffer = Buffer.from('6041000a0044021400420237', 'hex') 7 | const buffer = new XyoBuffer(inputBuffer) 8 | const struct = new XyoIterableStructure(buffer) 9 | const it = struct.newIterator() 10 | let i = 0 11 | 12 | while (it.hasNext()) { 13 | if (i === 0) { 14 | const bytes = it.next().value.getAll().getContentsCopy().toString('hex') 15 | expect(bytes).toEqual('00440214') 16 | } else if (i === 1) { 17 | const bytes = it.next().value.getAll().getContentsCopy().toString('hex') 18 | expect(bytes).toEqual('00420237') 19 | } else { 20 | throw new Error('Index does not exist') 21 | } 22 | 23 | i += 1 24 | } 25 | 26 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe( 27 | '0044021400420237' 28 | ) 29 | }) 30 | 31 | it('Typed 1', () => { 32 | const inputBuffer = Buffer.from('304107004402130237', 'hex') 33 | const buffer = new XyoBuffer(inputBuffer) 34 | const struct = new XyoIterableStructure(buffer) 35 | const it = struct.newIterator() 36 | let i = 0 37 | 38 | while (it.hasNext()) { 39 | if (i === 0) { 40 | const bytes = it.next().value.getAll().getContentsCopy().toString('hex') 41 | expect(bytes).toEqual('00440213') 42 | } else if (i === 1) { 43 | const bytes = it.next().value.getAll().getContentsCopy().toString('hex') 44 | expect(bytes).toEqual('00440237') 45 | } else { 46 | throw new Error('Index does not exist') 47 | } 48 | 49 | i += 1 50 | } 51 | 52 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe( 53 | '004402130237' 54 | ) 55 | }) 56 | 57 | it('Validate true', () => { 58 | const inputBuffer = Buffer.from( 59 | // tslint:disable-next-line:max-line-length 60 | '60020214201574201944000C415974B572A832CB601FBDAEC67E912BA9671B771E032E8F82BD97E9A2D57B7F05A222F820415A132CEE730579B7B245D97E58354EC304C64D97D3E6B4A77AE7213008240010217FBC8759E2B6AE0A12ADCBB6ABEEF342219AFDC495C8D920072AE09C784DCED800030500000E0520155C300624001021F45D1235377FDE3C42FF7953F6579A1C57164D24FAAFD643D332179D9F56675F3008240010217FBC8759E2B6AE0A12ADCBB6ABEEF342219AFDC495C8D920072AE09C784DCED800030500000101201906000E0300002017EF2005E42002E1201574201944000C415974B572A832CB601FBDAEC67E912BA9671B771E032E8F82BD97E9A2D57B7F05A222F820415A132CEE730579B7B245D97E58354EC304C64D97D3E6B4A77AE7213008240010215BF936EEDE11E006D9E2A0E2FD4EAEBB8F2B648C949F8E8DEEE1C9B6F4611D9C00030500000D0420151000030500000000201906000E030000201709201A06000B03000020174B201A4800094521008285A4FA3933F42CBE16967CDFA2C05799976E8BA5E28E071A1990C59510E3642100C192599BE091DE79CA25BE6F0D31B76653F91142E3541A98CD5261836C0D802C201A06000B03000020174B201A480009452100FCFB0708A2503595F14F12855D52C62570C5BB7E90635AC2B85C9B206A18D2492100D2A52F6A3F338C32D58EE89E432065B0D876ECB39FB1C34F41E2B3C1D74FCC99', 61 | 'hex' 62 | ) 63 | const buffer = new XyoBuffer(inputBuffer) 64 | const struct = new XyoIterableStructure(buffer) 65 | 66 | expect(XyoIterableStructure.validate(struct)).toBe(true) 67 | }) 68 | 69 | it('Validate false', () => { 70 | const inputBuffer = Buffer.from( 71 | // tslint:disable-next-line:max-line-length 72 | '600201A22015CB2019C8000C41170F9302323929FD3FD8A72851F73866A0BFC6D488040E9D921689E01B9E25E4393B0984576763DD9C5DA95E609A80B4CC12064758C1AEEE28AE264015BF474F000D8200AEB335766EC511499DDE566579B4ED1562079AA543388B2EDED68ED68363AE9DAE25E7E29B9A5607E670FF234B98891EE3FF99365A3CA6AB804173F1A8619934134A68F59FBDCA92E200C04A196D4A39C987C984E18B79D3EE81667DD92E962E6C630DB5D7BDCDB1988000A81713AB83E5D8B4EF6D2EAB4D70B61AADCA01E733CB0B3D072DE307CDBCD09F46D528A7159EB73DEBB018871E30D182F5BBB426689E758A7BFD4C51D0AD116CA621BF1C39DA49A837D525905D22BAB7C1874F6C7E6B4D56139A15C3BE1D1DC8E061C241C060A24B588217E37D6206AFE5D71F4698D42E25C4FCE996EECCF7690B900130200', 73 | 'hex' 74 | ) 75 | const buffer = new XyoBuffer(inputBuffer) 76 | const struct = new XyoIterableStructure(buffer) 77 | 78 | expect(XyoIterableStructure.validate(struct)).toBe(false) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /src/object-model/spec/xyo-schema-test.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoSchema } from '../xyo-schema' 2 | import { XyoSize } from '../xyo-size' 3 | 4 | describe('XyoSchema', () => { 5 | it('Size as 1', () => { 6 | const schema = new XyoSchema(0xff, 0x20) 7 | 8 | expect(schema.getSizeIdentifier()).toBe(XyoSize.ONE) 9 | }) 10 | 11 | it('Size as 2', () => { 12 | const schema = new XyoSchema(0xff, 0x60) 13 | 14 | expect(schema.getSizeIdentifier()).toBe(XyoSize.TWO) 15 | }) 16 | 17 | it('Size as 4', () => { 18 | const schema = new XyoSchema(0xff, 0xa0) 19 | 20 | expect(schema.getSizeIdentifier()).toBe(XyoSize.FOUR) 21 | }) 22 | 23 | it('Size as 8', () => { 24 | const schema = new XyoSchema(0xff, 0xe0) 25 | 26 | expect(schema.getSizeIdentifier()).toBe(XyoSize.EIGHT) 27 | }) 28 | 29 | it('Iterable flag set', () => { 30 | const schema = new XyoSchema(0xff, 0x20) 31 | 32 | expect(schema.getIsIterable()).toBe(true) 33 | }) 34 | 35 | it('Iterable flag not set', () => { 36 | const schema = new XyoSchema(0xff, 0x00) 37 | 38 | expect(schema.getIsIterable()).toBe(false) 39 | }) 40 | 41 | it('Id', () => { 42 | const schema = new XyoSchema(0x24, 0x00) 43 | 44 | expect(schema.id).toBe(0x24) 45 | }) 46 | 47 | it('Create case 1', () => { 48 | const schema = XyoSchema.create(0xff, false, false, XyoSize.ONE) 49 | 50 | expect(schema.id).toBe(0xff) 51 | expect(schema.getIsIterable()).toBe(false) 52 | expect(schema.getIsTypedIterable()).toBe(false) 53 | expect(schema.getSizeIdentifier()).toBe(XyoSize.ONE) 54 | }) 55 | 56 | it('Create case 2', () => { 57 | const schema = XyoSchema.create(0xff, false, false, XyoSize.TWO) 58 | 59 | expect(schema.id).toBe(0xff) 60 | expect(schema.getIsIterable()).toBe(false) 61 | expect(schema.getIsTypedIterable()).toBe(false) 62 | expect(schema.getSizeIdentifier()).toBe(XyoSize.TWO) 63 | }) 64 | 65 | it('Create case 3', () => { 66 | const schema = XyoSchema.create(0xff, true, true, XyoSize.EIGHT) 67 | 68 | expect(schema.id).toBe(0xff) 69 | expect(schema.getIsIterable()).toBe(true) 70 | expect(schema.getIsTypedIterable()).toBe(true) 71 | expect(schema.getSizeIdentifier()).toBe(XyoSize.EIGHT) 72 | }) 73 | 74 | it('Create case 4', () => { 75 | const schema = XyoSchema.create(0xff, false, true, XyoSize.FOUR) 76 | 77 | expect(schema.id).toBe(0xff) 78 | expect(schema.getIsIterable()).toBe(false) 79 | expect(schema.getIsTypedIterable()).toBe(true) 80 | expect(schema.getSizeIdentifier()).toBe(XyoSize.FOUR) 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /src/object-model/spec/xyo-structure.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer } from '../xyo-buffer' 2 | import { XyoSchema } from '../xyo-schema' 3 | import { XyoSize } from '../xyo-size' 4 | import { XyoStructure } from '../xyo-structure' 5 | 6 | describe('XyoBuffer', () => { 7 | it('1 byte size', () => { 8 | const inputBuffer = Buffer.from('0055031337', 'hex') 9 | const buffer = new XyoBuffer(inputBuffer) 10 | const struct = new XyoStructure(buffer) 11 | 12 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe('1337') 13 | }) 14 | 15 | it('2 byte size', () => { 16 | const inputBuffer = Buffer.from('405500041337', 'hex') 17 | const buffer = new XyoBuffer(inputBuffer) 18 | const struct = new XyoStructure(buffer) 19 | 20 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe('1337') 21 | }) 22 | 23 | it('4 byte size', () => { 24 | const inputBuffer = Buffer.from('8055000000061337', 'hex') 25 | const buffer = new XyoBuffer(inputBuffer) 26 | const struct = new XyoStructure(buffer) 27 | 28 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe('1337') 29 | }) 30 | 31 | it('8 byte size', () => { 32 | const inputBuffer = Buffer.from('c055000000000000000a1337', 'hex') 33 | const buffer = new XyoBuffer(inputBuffer) 34 | const struct = new XyoStructure(buffer) 35 | 36 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe('1337') 37 | }) 38 | 39 | it('Schema test 1', () => { 40 | const inputBuffer = Buffer.from('0055031337', 'hex') 41 | const buffer = new XyoBuffer(inputBuffer) 42 | const struct = new XyoStructure(buffer) 43 | 44 | expect(struct.getSchema().id).toBe(0x55) 45 | expect(struct.getSchema().getIsIterable()).toBe(false) 46 | expect(struct.getSchema().getIsTypedIterable()).toBe(false) 47 | expect(struct.getSchema().getSizeIdentifier()).toBe(XyoSize.ONE) 48 | }) 49 | 50 | it('Encode 1 byte size', () => { 51 | const schema = new XyoSchema(0, 0) 52 | const inputBuffer = Buffer.from('1337', 'hex') 53 | const buffer = new XyoBuffer(inputBuffer) 54 | const struct = XyoStructure.newInstance(schema, buffer) 55 | 56 | expect(struct.getAll().getContentsCopy().toString('hex')).toBe('0000031337') 57 | expect(struct.getValue().getContentsCopy().toString('hex')).toBe('1337') 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /src/object-model/xyo-buffer.ts: -------------------------------------------------------------------------------- 1 | export class XyoBuffer { 2 | public static wrap( 3 | buffer: XyoBuffer, 4 | startOffset: number, 5 | endOffset: number 6 | ): XyoBuffer { 7 | return new XyoBuffer( 8 | buffer.rootBuffer, 9 | startOffset + buffer.startOffset, 10 | endOffset + buffer.startOffset 11 | ) 12 | } 13 | 14 | public startOffset: number 15 | public endOffset: number 16 | public rootBuffer: Buffer 17 | 18 | constructor(buffer: Buffer, startOffset?: number, endOffset?: number) { 19 | this.rootBuffer = buffer 20 | this.startOffset = startOffset || 0 21 | this.endOffset = endOffset || buffer.length 22 | } 23 | 24 | public getSize(): number { 25 | return this.endOffset - this.startOffset 26 | } 27 | 28 | public getUInt8(offset: number) { 29 | return this.rootBuffer.readUInt8(this.startOffset + offset) 30 | } 31 | 32 | public getUInt16BE(offset: number) { 33 | return this.rootBuffer.readUInt16BE(this.startOffset + offset) 34 | } 35 | 36 | public getUInt32BE(offset: number) { 37 | return this.rootBuffer.readUInt32BE(this.startOffset + offset) 38 | } 39 | 40 | public getUInt64BE(offset: number) { 41 | // todo find way to get the int64 size, this will get all the way up to a 2^32 as a size 42 | return this.rootBuffer.readUInt32BE(this.startOffset + offset + 4) 43 | } 44 | 45 | public getContentsCopy(): Buffer { 46 | return this.rootBuffer.slice(this.startOffset, this.endOffset) 47 | } 48 | 49 | public copyRangeOf(from: number, to: number): XyoBuffer { 50 | return XyoBuffer.wrap(this, from, to) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/object-model/xyo-iterable-structure.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer } from './xyo-buffer' 2 | // eslint-disable-next-line import/no-cycle 3 | import { XyoIterator } from './xyo-iterator' 4 | import { XyoSchema } from './xyo-schema' 5 | import { XyoStructure } from './xyo-structure' 6 | 7 | export class XyoIterableStructure extends XyoStructure { 8 | public static validate(structure: XyoIterableStructure) { 9 | const it = structure.newIterator() 10 | 11 | try { 12 | while (it.hasNext()) { 13 | const item = it.next().value 14 | 15 | if (item instanceof XyoIterableStructure) { 16 | if (!XyoIterableStructure.validate(item)) { 17 | return false 18 | } 19 | } 20 | } 21 | return true 22 | } catch (error) { 23 | return false 24 | } 25 | } 26 | 27 | public static toJson(structure: XyoStructure): any[] { 28 | const toReturn: any[] = [] 29 | 30 | if (structure instanceof XyoIterableStructure) { 31 | const it = structure.newIterator() 32 | 33 | while (it.hasNext()) { 34 | const item = it.next().value 35 | const childArray = XyoIterableStructure.toJson(item) 36 | toReturn.push(childArray) 37 | } 38 | } else { 39 | toReturn.push(structure.getAll().getContentsCopy().toString('hex')) 40 | } 41 | 42 | return toReturn 43 | } 44 | 45 | public static newIterable( 46 | schema: XyoSchema, 47 | items: XyoStructure[] 48 | ): XyoIterableStructure { 49 | if (!schema.getIsIterable()) { 50 | throw new Error('Can not make iterable object from not iterable schema') 51 | } 52 | 53 | if (schema.getIsTypedIterable()) { 54 | const typedBuffer = XyoIterableStructure.encodeTyped(schema, items) 55 | return new XyoIterableStructure( 56 | XyoStructure.encode(schema, new XyoBuffer(typedBuffer)) 57 | ) 58 | } 59 | 60 | const untypedBuffer = XyoIterableStructure.encodeUntyped(schema, items) 61 | return new XyoIterableStructure( 62 | XyoStructure.encode(schema, new XyoBuffer(untypedBuffer)) 63 | ) 64 | } 65 | 66 | private static encodeUntyped( 67 | schema: XyoSchema, 68 | items: XyoStructure[] 69 | ): Buffer { 70 | const buffersToMerge: Buffer[] = [] 71 | 72 | for (const item of items) { 73 | buffersToMerge.push(item.getAll().getContentsCopy()) 74 | } 75 | 76 | return Buffer.concat(buffersToMerge) 77 | } 78 | 79 | private static encodeTyped(schema: XyoSchema, items: XyoStructure[]): Buffer { 80 | const buffersToMerge: Buffer[] = [] 81 | 82 | if (items.length < 1) { 83 | return Buffer.alloc(0) 84 | } 85 | 86 | const headerBuffer = Buffer.alloc(2) 87 | headerBuffer.writeUInt8(items[0].getSchema().encodingCatalogue, 0) 88 | headerBuffer.writeUInt8(items[0].getSchema().id, 1) 89 | buffersToMerge.push(headerBuffer) 90 | 91 | for (const item of items) { 92 | buffersToMerge.push(item.getAll().getContentsCopy().slice(2)) 93 | } 94 | 95 | return Buffer.concat(buffersToMerge) 96 | } 97 | 98 | private typedSchema: XyoSchema | undefined 99 | 100 | public newIterator(): XyoIterator { 101 | return new XyoIterator( 102 | this.readOwnHeader(), 103 | this, 104 | this.typedSchema !== undefined 105 | ) 106 | } 107 | 108 | public getCount(): number { 109 | const it = this.newIterator() 110 | let i = 0 111 | 112 | while (it.hasNext()) { 113 | it.next() 114 | i++ 115 | } 116 | 117 | return i 118 | } 119 | 120 | public get(index: number): XyoStructure { 121 | const it = this.newIterator() 122 | let i = 0 123 | 124 | while (it.hasNext()) { 125 | const item = it.next() 126 | 127 | if (index === i) { 128 | return item.value 129 | } 130 | 131 | i++ 132 | } 133 | 134 | throw new Error(`Out of index ${index}`) 135 | } 136 | 137 | public getId(id: number): XyoStructure[] { 138 | const itemsOfThatId: XyoStructure[] = [] 139 | const it = this.newIterator() 140 | 141 | while (it.hasNext()) { 142 | const item = it.next().value 143 | 144 | if (item.getSchema().id === id) { 145 | itemsOfThatId.push(item) 146 | } 147 | } 148 | 149 | return itemsOfThatId 150 | } 151 | 152 | public readItemAtOffset(offset: number) { 153 | if (this.typedSchema) { 154 | return this.readItemTyped(offset, this.typedSchema) 155 | } 156 | 157 | return this.readItemUntyped(offset) 158 | } 159 | 160 | protected addElement(element: XyoStructure) { 161 | this.readOwnHeader() 162 | 163 | if (element.getSchema().getIsTypedIterable() && this.typedSchema) { 164 | if (element.getSchema().id === this.typedSchema.id) { 165 | const newBufferTyped = Buffer.concat([ 166 | this.getValue().getContentsCopy(), 167 | element.getAll().getContentsCopy().slice(2), 168 | ]) 169 | 170 | this.contents = XyoStructure.encode( 171 | element.getSchema(), 172 | new XyoBuffer(newBufferTyped) 173 | ) 174 | return 175 | } 176 | 177 | throw new Error('Can not add different type to typed array') 178 | } 179 | 180 | const newBufferUntyped = Buffer.concat([ 181 | this.getValue().getContentsCopy(), 182 | element.getAll().getContentsCopy(), 183 | ]) 184 | 185 | this.contents = XyoStructure.encode( 186 | this.getSchema(), 187 | new XyoBuffer(newBufferUntyped) 188 | ) 189 | } 190 | 191 | private readItemUntyped(offset: number): XyoStructure { 192 | const schema = this.readSchema(offset) 193 | const sizeOfObject = this.readSize(schema.getSizeIdentifier(), offset + 2) 194 | 195 | if (sizeOfObject === 0) { 196 | throw new Error('Size can not be 0') 197 | } 198 | 199 | const end = 2 + sizeOfObject + offset 200 | this.checkIndex(end) 201 | 202 | const objectValue = XyoBuffer.wrap(this.contents, offset, end) 203 | 204 | if (schema.getIsIterable()) { 205 | return new XyoIterableStructure(objectValue) 206 | } 207 | 208 | return new XyoStructure(objectValue) 209 | } 210 | 211 | private readItemTyped(offset: number, schemaOfItem: XyoSchema): XyoStructure { 212 | const sizeOfObject = this.readSize(schemaOfItem.getSizeIdentifier(), offset) 213 | 214 | if (sizeOfObject === 0) { 215 | throw new Error('Size can not be 0') 216 | } 217 | 218 | const end = sizeOfObject + offset 219 | this.checkIndex(end) 220 | 221 | const objectValue = XyoBuffer.wrap(this.contents, offset, end) 222 | 223 | if (schemaOfItem.getIsIterable()) { 224 | return new XyoIterableStructure(objectValue, schemaOfItem) 225 | } 226 | 227 | return new XyoStructure(objectValue, schemaOfItem) 228 | } 229 | 230 | private readOwnHeader(): number { 231 | this.checkIndex(2) 232 | const schema = this.getSchema() 233 | 234 | this.checkIndex(2 + schema.getSizeIdentifier().valueOf() - 1) 235 | const totalSize = this.readSize(schema.getSizeIdentifier(), 2) 236 | 237 | if (!schema.getIsIterable()) { 238 | throw new Error('Object is not iterable') 239 | } 240 | 241 | if ( 242 | schema.getIsTypedIterable() && 243 | totalSize !== schema.getSizeIdentifier().valueOf() 244 | ) { 245 | this.typedSchema = this.readSchema( 246 | schema.getSizeIdentifier().valueOf() + 2 247 | ) 248 | return 4 + schema.getSizeIdentifier().valueOf() 249 | } 250 | 251 | return 2 + schema.getSizeIdentifier().valueOf() 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/object-model/xyo-iterator.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-cycle 2 | import { XyoIterableStructure } from './xyo-iterable-structure' 3 | import { XyoStructure } from './xyo-structure' 4 | 5 | export class XyoIterator implements Iterator { 6 | private offset: number 7 | private structure: XyoIterableStructure 8 | private isTyped: boolean 9 | 10 | constructor( 11 | offset: number, 12 | structure: XyoIterableStructure, 13 | isTyped: boolean 14 | ) { 15 | this.offset = offset 16 | this.structure = structure 17 | this.isTyped = isTyped 18 | } 19 | 20 | public next(): IteratorResult { 21 | const nextItem = this.structure.readItemAtOffset(this.offset) 22 | 23 | if (this.isTyped) { 24 | this.offset += nextItem.getAll().getSize() - 2 25 | } else { 26 | this.offset += nextItem.getAll().getSize() 27 | } 28 | 29 | const result: IteratorResult = { 30 | done: this.hasNext(), 31 | value: nextItem, 32 | } 33 | 34 | return result 35 | } 36 | 37 | public hasNext(): boolean { 38 | return this.structure.getAll().getSize() > this.offset 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/object-model/xyo-schema.ts: -------------------------------------------------------------------------------- 1 | import { XyoSize } from './xyo-size' 2 | 3 | export class XyoSchema { 4 | public static create( 5 | id: number, 6 | isIterable: boolean, 7 | isTyped: boolean, 8 | sizeIdentifier: XyoSize 9 | ): XyoSchema { 10 | const iterableByte = XyoSchema.getIterableByte(isIterable) 11 | const getTypedByte = XyoSchema.getTypedByte(isTyped) 12 | const getSizeByte = XyoSchema.getSizeByte(sizeIdentifier) 13 | const encodingCatalogue = iterableByte | getTypedByte | getSizeByte 14 | 15 | return new XyoSchema(id, encodingCatalogue) 16 | } 17 | 18 | private static getIterableByte(isIterable: boolean): number { 19 | if (isIterable) { 20 | return 0x20 21 | } 22 | 23 | return 0x00 24 | } 25 | 26 | private static getTypedByte(isTyped: boolean): number { 27 | if (isTyped) { 28 | return 0x10 29 | } 30 | 31 | return 0x00 32 | } 33 | 34 | private static getSizeByte(size: XyoSize): number { 35 | switch (size) { 36 | case XyoSize.ONE: 37 | return 0x00 38 | case XyoSize.TWO: 39 | return 0x40 40 | case XyoSize.FOUR: 41 | return 0x80 42 | case XyoSize.EIGHT: 43 | return 0xc0 44 | } 45 | } 46 | 47 | public id: number 48 | public encodingCatalogue: number 49 | 50 | constructor(id: number, encodingCatalogue: number) { 51 | this.id = id 52 | this.encodingCatalogue = encodingCatalogue 53 | } 54 | 55 | public getSizeIdentifier(): XyoSize { 56 | if ((this.encodingCatalogue & 0xc0) === 0x00) { 57 | return XyoSize.ONE 58 | } 59 | 60 | if ((this.encodingCatalogue & 0xc0) === 0x40) { 61 | return XyoSize.TWO 62 | } 63 | 64 | if ((this.encodingCatalogue & 0xc0) === 0x80) { 65 | return XyoSize.FOUR 66 | } 67 | 68 | return XyoSize.EIGHT 69 | } 70 | 71 | public getIsIterable(): boolean { 72 | return (this.encodingCatalogue & 0x20) !== 0 73 | } 74 | 75 | public getIsTypedIterable(): boolean { 76 | return (this.encodingCatalogue & 0x10) !== 0 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/object-model/xyo-size.ts: -------------------------------------------------------------------------------- 1 | export enum XyoSize { 2 | ONE = 1, 3 | TWO = 2, 4 | FOUR = 4, 5 | EIGHT = 8, 6 | } 7 | -------------------------------------------------------------------------------- /src/object-model/xyo-structure.ts: -------------------------------------------------------------------------------- 1 | import { XyoSizeUtil } from './size-util' 2 | import { XyoBuffer } from './xyo-buffer' 3 | import { XyoSchema } from './xyo-schema' 4 | import { XyoSize } from './xyo-size' 5 | 6 | export class XyoStructure { 7 | public static encode(schema: XyoSchema, value: XyoBuffer): XyoBuffer { 8 | const bestSize = XyoSizeUtil.getBestSize(value.getSize()) 9 | const header = Buffer.alloc(2 + bestSize.valueOf()) 10 | const size = value.getSize() + bestSize.valueOf() 11 | const optimizedSchema = XyoSchema.create( 12 | schema.id, 13 | schema.getIsIterable(), 14 | schema.getIsTypedIterable(), 15 | bestSize 16 | ) 17 | 18 | header.writeUInt8(optimizedSchema.encodingCatalogue, 0) 19 | header.writeUInt8(schema.id, 1) 20 | 21 | switch (bestSize) { 22 | case XyoSize.ONE: 23 | header.writeUInt8(size, 2) 24 | break 25 | case XyoSize.TWO: 26 | header.writeUInt16BE(size, 2) 27 | break 28 | case XyoSize.FOUR: 29 | header.writeUInt32BE(size, 2) 30 | break 31 | case XyoSize.EIGHT: 32 | header.writeUIntBE(size, 2, 8) 33 | break 34 | } 35 | 36 | const encoded = Buffer.concat([header, value.getContentsCopy()]) 37 | return new XyoBuffer(encoded) 38 | } 39 | 40 | public static newInstance(schema: XyoSchema, value: XyoBuffer) { 41 | return new XyoStructure(XyoStructure.encode(schema, value)) 42 | } 43 | 44 | protected contents: XyoBuffer 45 | private overrideSchema: XyoSchema | undefined 46 | 47 | constructor(contents: XyoBuffer | Buffer, overrideSchema?: XyoSchema) { 48 | this.contents = 49 | contents instanceof Buffer 50 | ? (this.contents = new XyoBuffer(contents)) 51 | : contents 52 | 53 | this.overrideSchema = overrideSchema 54 | } 55 | 56 | public getSchema(): XyoSchema { 57 | if (this.overrideSchema) { 58 | return this.overrideSchema 59 | } 60 | 61 | return this.readSchema(0) 62 | } 63 | 64 | public getValue(): XyoBuffer { 65 | if (this.overrideSchema) { 66 | const schema = this.getSchema() 67 | const startOffset = schema.getSizeIdentifier().valueOf() 68 | const endOffset = this.readSize(schema.getSizeIdentifier(), 0) 69 | return this.contents.copyRangeOf(startOffset, endOffset) 70 | } 71 | 72 | const schema = this.getSchema() 73 | const startOffset = schema.getSizeIdentifier().valueOf() + 2 74 | const endOffset = this.readSize(schema.getSizeIdentifier(), 2) + 2 75 | 76 | return this.contents.copyRangeOf(startOffset, endOffset) 77 | } 78 | 79 | public getAll(): XyoBuffer { 80 | if (this.overrideSchema) { 81 | const headerBuffer = Buffer.alloc(2) 82 | headerBuffer.writeUInt8(this.overrideSchema.encodingCatalogue, 0) 83 | headerBuffer.writeUInt8(this.overrideSchema.id, 1) 84 | const together = Buffer.concat([ 85 | headerBuffer, 86 | this.contents.getContentsCopy(), 87 | ]) 88 | 89 | return new XyoBuffer(together) 90 | } 91 | 92 | return this.contents 93 | } 94 | 95 | protected readSchema(offset: number): XyoSchema { 96 | this.checkIndex(offset + 2) 97 | 98 | const id = this.contents.getUInt8(1 + offset) 99 | const encodingCatalogue = this.contents.getUInt8(offset) 100 | 101 | return new XyoSchema(id, encodingCatalogue) 102 | } 103 | 104 | protected checkIndex(index: number) { 105 | if (index > this.contents.getSize()) { 106 | throw new Error(`Out of count ${index}`) 107 | } 108 | } 109 | 110 | protected readSize(size: XyoSize, offset: number): number { 111 | switch (size) { 112 | case XyoSize.ONE: 113 | return this.contents.getUInt8(offset) 114 | case XyoSize.TWO: 115 | return this.contents.getUInt16BE(offset) 116 | case XyoSize.FOUR: 117 | return this.contents.getUInt32BE(offset) 118 | case XyoSize.EIGHT: 119 | return this.contents.getUInt64BE(offset) 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/origin/index.ts: -------------------------------------------------------------------------------- 1 | import XyoPayloadConstructor from './xyo-payload-constructor' 2 | 3 | export { XyoBoundWitnessInserter } from './xyo-bound-witness-inserter' 4 | export { XyoGenesisBlockCreator } from './xyo-genesis-block-creator' 5 | export { XyoOriginPayloadConstructor } from './xyo-origin-payload-constructor' 6 | export { XyoOriginState } from './xyo-origin-state' 7 | 8 | export { XyoPayloadConstructor } 9 | -------------------------------------------------------------------------------- /src/origin/xyo-bound-witness-inserter.ts: -------------------------------------------------------------------------------- 1 | import { XyoBase } from '@xyo-network/sdk-base-nodejs' 2 | import bs58 from 'bs58' 3 | 4 | import { XyoBoundWitness } from '../bound-witness' 5 | import { XyoHasher } from '../hashing' 6 | import { XyoIterableStructure, XyoSchema, XyoStructure } from '../object-model' 7 | import { XyoOriginBlockRepository } from '../persist/xyo-origin-block-repository' 8 | import { XyoObjectSchema } from '../schema' 9 | import { XyoOriginState } from './xyo-origin-state' 10 | 11 | export class XyoBoundWitnessInserter extends XyoBase { 12 | private hasher: XyoHasher 13 | private state: XyoOriginState 14 | private blockRepository: XyoOriginBlockRepository 15 | private blockListeners: { [key: string]: (boundWitness: Buffer) => void } = {} 16 | 17 | constructor( 18 | hasher: XyoHasher, 19 | state: XyoOriginState, 20 | blockRepo: XyoOriginBlockRepository 21 | ) { 22 | super() 23 | this.hasher = hasher 24 | this.state = state 25 | this.blockRepository = blockRepo 26 | } 27 | 28 | public addBlockListener( 29 | key: string, 30 | listener: (boundWitness: Buffer) => void 31 | ) { 32 | this.blockListeners[key] = listener 33 | } 34 | 35 | public removeBlockListener(key: string) { 36 | delete this.blockListeners[key] 37 | } 38 | 39 | public async insert(boundWitness: XyoBoundWitness) { 40 | const bridgeBlocks = this.getNestedObjectType( 41 | boundWitness, 42 | XyoObjectSchema.WITNESS, 43 | XyoObjectSchema.BRIDGE_BLOCK_SET 44 | ) 45 | const bridgeBlocksHashes = this.getNestedObjectType( 46 | boundWitness, 47 | XyoObjectSchema.FETTER, 48 | XyoObjectSchema.BRIDGE_HASH_SET 49 | ) 50 | const rootBlockWithoutBridgedBlocks = this.removeBridgeBlocks(boundWitness) 51 | const hash = boundWitness.getHash(this.hasher) 52 | this.state.addOriginBlock(hash) 53 | 54 | this.logInfo( 55 | `Inserted new origin block with hash: ${bs58.encode( 56 | hash.getAll().getContentsCopy() 57 | )}` 58 | ) 59 | 60 | await this.state.repo.commit() 61 | 62 | // no need to await the add block, this can be async 63 | this.blockRepository.addOriginBlock( 64 | hash.getAll().getContentsCopy(), 65 | rootBlockWithoutBridgedBlocks.getAll().getContentsCopy() 66 | ) 67 | 68 | this.logInfo(`Origin state at new height: ${this.state.getIndexAsNumber()}`) 69 | 70 | if (bridgeBlocks && bridgeBlocksHashes) { 71 | // no need to await the add block, this can be async 72 | this.blockRepository.addOriginBlocks( 73 | bridgeBlocksHashes.getAll().getContentsCopy(), 74 | bridgeBlocks.getAll().getContentsCopy() 75 | ) 76 | } 77 | 78 | for (const [, value] of Object.entries(this.blockListeners)) { 79 | value(boundWitness.getAll().getContentsCopy()) 80 | } 81 | } 82 | 83 | private getNestedObjectType( 84 | boundWitness: XyoBoundWitness, 85 | rootSchema: XyoSchema, 86 | subSchema: XyoSchema 87 | ): XyoStructure | undefined { 88 | const it = boundWitness.newIterator() 89 | 90 | while (it.hasNext()) { 91 | const bwItem = it.next().value 92 | 93 | if ( 94 | bwItem.getSchema().id === rootSchema.id && 95 | bwItem instanceof XyoIterableStructure 96 | ) { 97 | const fetterIt = bwItem.newIterator() 98 | 99 | while (fetterIt.hasNext()) { 100 | const fetterItem = fetterIt.next().value 101 | 102 | if (fetterItem.getSchema().id === subSchema.id) { 103 | return fetterItem 104 | } 105 | } 106 | } 107 | } 108 | 109 | return 110 | } 111 | 112 | private removeBridgeBlocks( 113 | boundWitness: XyoBoundWitness 114 | ): XyoIterableStructure { 115 | const newLedgerItems: XyoStructure[] = [] 116 | const it = boundWitness.newIterator() 117 | 118 | while (it.hasNext()) { 119 | const bwItem = it.next().value 120 | 121 | if ( 122 | bwItem.getSchema().id === XyoObjectSchema.WITNESS.id && 123 | bwItem instanceof XyoIterableStructure 124 | ) { 125 | const witnessIt = bwItem.newIterator() 126 | const newWitnessItems: XyoStructure[] = [] 127 | 128 | while (witnessIt.hasNext()) { 129 | const witnessItem = witnessIt.next().value 130 | 131 | if ( 132 | witnessItem.getSchema().id !== XyoObjectSchema.BRIDGE_BLOCK_SET.id 133 | ) { 134 | newWitnessItems.push(witnessItem) 135 | } 136 | } 137 | 138 | newLedgerItems.push( 139 | XyoIterableStructure.newIterable( 140 | XyoObjectSchema.WITNESS, 141 | newWitnessItems 142 | ) 143 | ) 144 | } else { 145 | newLedgerItems.push(bwItem) 146 | } 147 | } 148 | 149 | return XyoIterableStructure.newIterable(XyoObjectSchema.BW, newLedgerItems) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/origin/xyo-genesis-block-creator.ts: -------------------------------------------------------------------------------- 1 | import { XyoBoundWitness, XyoZigZagBoundWitness } from '../bound-witness' 2 | import { XyoSigner } from '../signing' 3 | import XyoPayloadConstructor from './xyo-payload-constructor' 4 | 5 | export class XyoGenesisBlockCreator { 6 | public static async create( 7 | signers: XyoSigner[], 8 | payloadProvider: XyoPayloadConstructor 9 | ): Promise { 10 | const payload = await payloadProvider.getPayloads(Buffer.alloc(0)) 11 | const boundWitness = new XyoZigZagBoundWitness( 12 | signers, 13 | payload.signed, 14 | payload.unsigned 15 | ) 16 | boundWitness.incomingData(undefined, true) 17 | return boundWitness 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/origin/xyo-origin-payload-constructor.ts: -------------------------------------------------------------------------------- 1 | import { IXyoBoundWitnessPayload } from '../heuristics' 2 | import { IXyoHeuristicGetter } from '../heuristics/xyo-heuristic-getter' 3 | import { XyoStructure } from '../object-model' 4 | import { XyoOriginState } from './xyo-origin-state' 5 | import XyoPayloadConstructor from './xyo-payload-constructor' 6 | 7 | export class XyoOriginPayloadConstructor implements XyoPayloadConstructor { 8 | private originState: XyoOriginState 9 | private heuristicGetters: Map = new Map() 10 | 11 | constructor(originState: XyoOriginState) { 12 | this.originState = originState 13 | } 14 | 15 | public addHeuristicGetter(key: string, getter: IXyoHeuristicGetter) { 16 | this.heuristicGetters.set(key, getter) 17 | } 18 | 19 | public removeHeuristicGetter(key: string) { 20 | this.heuristicGetters.delete(key) 21 | } 22 | 23 | // eslint-disable-next-line require-await 24 | public async getPayloads(choice: Buffer): Promise { 25 | const originItems = this.getOriginItems() 26 | const getterItems = this.getAllHeuristics(choice) 27 | 28 | const payload: IXyoBoundWitnessPayload = { 29 | signed: originItems.concat(getterItems), 30 | unsigned: [], 31 | } 32 | 33 | return payload 34 | } 35 | 36 | private getAllHeuristics(choice: Buffer): XyoStructure[] { 37 | const toReturn: XyoStructure[] = [] 38 | 39 | this.heuristicGetters.forEach((value, _) => { 40 | const item = value.getHeuristic(choice) 41 | 42 | if (item) { 43 | toReturn.push(item) 44 | } 45 | }) 46 | 47 | return toReturn 48 | } 49 | 50 | private getOriginItems(): XyoStructure[] { 51 | const toReturn: XyoStructure[] = [] 52 | 53 | const index = this.originState.getIndex() 54 | const previousHash = this.originState.getPreviousHash() 55 | const nextPublicKey = this.originState.getNextPublicKey() 56 | 57 | toReturn.push(index) 58 | 59 | if (previousHash) { 60 | toReturn.push(previousHash) 61 | } 62 | 63 | if (nextPublicKey) { 64 | toReturn.push(nextPublicKey) 65 | } 66 | 67 | return toReturn 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/origin/xyo-origin-state.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer, XyoIterableStructure, XyoStructure } from '../object-model' 2 | import XyoOriginStateRepository from '../persist/xyo-origin-state-repository' 3 | import { XyoObjectSchema } from '../schema' 4 | import XyoSigner from '../signing/xyo-signer' 5 | 6 | export class XyoOriginState { 7 | public static createNextPublicKey(publicKey: XyoStructure): XyoStructure { 8 | return XyoStructure.newInstance( 9 | XyoObjectSchema.NEXT_PUBLIC_KEY, 10 | publicKey.getAll() 11 | ) 12 | } 13 | 14 | public static createPreviousHash(hash: XyoStructure): XyoStructure { 15 | return XyoIterableStructure.newIterable(XyoObjectSchema.PREVIOUS_HASH, [ 16 | hash, 17 | ]) 18 | } 19 | 20 | public static createIndex(index: number): XyoStructure { 21 | const numberBuffer = Buffer.alloc(4) 22 | numberBuffer.writeUInt32BE(index, 0) 23 | return XyoStructure.newInstance( 24 | XyoObjectSchema.INDEX, 25 | new XyoBuffer(numberBuffer) 26 | ) 27 | } 28 | 29 | public repo: XyoOriginStateRepository 30 | private waitingSigners: XyoSigner[] = [] 31 | private nextPublicKey: XyoStructure | undefined 32 | 33 | constructor(repo: XyoOriginStateRepository) { 34 | this.repo = repo 35 | } 36 | 37 | public getIndexAsNumber(): number { 38 | return this.getIndex().getValue().getContentsCopy().readUInt32BE(0) 39 | } 40 | 41 | public getNextPublicKey(): XyoStructure | undefined { 42 | return this.nextPublicKey 43 | } 44 | 45 | public getIndex(): XyoStructure { 46 | const indexInStore = this.repo.getIndex() 47 | 48 | if (indexInStore) { 49 | return indexInStore 50 | } 51 | 52 | return XyoOriginState.createIndex(0) 53 | } 54 | 55 | public getPreviousHash(): XyoStructure | undefined { 56 | return this.repo.getPreviousHash() 57 | } 58 | 59 | public removeOldestSigner() { 60 | this.repo.removeOldestSigner() 61 | } 62 | 63 | public getSigners() { 64 | return this.repo.getSigners() 65 | } 66 | 67 | public addSigner(signer: XyoSigner) { 68 | const index = this.getIndex().getValue().getContentsCopy().readUInt32BE(0) 69 | 70 | if (index === 0) { 71 | this.repo.addSigner(signer) 72 | return 73 | } 74 | 75 | this.waitingSigners.push(signer) 76 | this.nextPublicKey = XyoOriginState.createNextPublicKey( 77 | signer.getPublicKey() 78 | ) 79 | } 80 | 81 | public addOriginBlock(hash: XyoStructure) { 82 | const previousHash = XyoOriginState.createPreviousHash(hash) 83 | this.nextPublicKey = undefined 84 | this.addWaitingSigner() 85 | this.repo.putPreviousHash(previousHash) 86 | this.incrementIndex() 87 | } 88 | 89 | private addWaitingSigner() { 90 | if (this.waitingSigners.length > 0) { 91 | this.repo.addSigner(this.waitingSigners[0]) 92 | this.waitingSigners.shift() 93 | } 94 | } 95 | 96 | private incrementIndex() { 97 | const index = this.getIndex().getValue().getContentsCopy().readUInt32BE(0) 98 | const newIndex = XyoOriginState.createIndex(index + 1) 99 | this.repo.putIndex(newIndex) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/origin/xyo-payload-constructor.ts: -------------------------------------------------------------------------------- 1 | import { IXyoBoundWitnessPayload } from '../heuristics' 2 | 3 | abstract class XyoPayloadConstructor { 4 | abstract getPayloads(choice: Buffer): Promise 5 | } 6 | 7 | export default XyoPayloadConstructor 8 | -------------------------------------------------------------------------------- /src/persist/index.ts: -------------------------------------------------------------------------------- 1 | import XyoOriginStateRepository from './xyo-origin-state-repository' 2 | export { XyoFileOriginStateRepository } from './xyo-file-origin-state-repository' 3 | export { XyoMemoryBlockRepository } from './xyo-memory-block-repository' 4 | export * from './xyo-origin-block-repository' 5 | export { XyoOriginStateRepository } 6 | -------------------------------------------------------------------------------- /src/persist/spec/xyo-origin-state-repository.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer, XyoStructure } from '../../object-model' 2 | import { XyoObjectSchema } from '../../schema' 3 | import { XyoFileOriginStateRepository } from '../xyo-file-origin-state-repository' 4 | import XyoOriginStateRepository from '../xyo-origin-state-repository' 5 | 6 | function testOriginStateRepository(repo: XyoOriginStateRepository) { 7 | describe('IXyoOriginStateRepository interface', () => { 8 | it('Should validate', async () => { 9 | const testIndex0 = XyoStructure.newInstance( 10 | XyoObjectSchema.INDEX, 11 | new XyoBuffer(Buffer.from('00')) 12 | ) 13 | const testIndex1 = XyoStructure.newInstance( 14 | XyoObjectSchema.INDEX, 15 | new XyoBuffer(Buffer.from('01')) 16 | ) 17 | 18 | const testPreviousHash0 = XyoStructure.newInstance( 19 | XyoObjectSchema.PREVIOUS_HASH, 20 | new XyoBuffer(Buffer.from('00')) 21 | ) 22 | const testPreviousHash1 = XyoStructure.newInstance( 23 | XyoObjectSchema.PREVIOUS_HASH, 24 | new XyoBuffer(Buffer.from('01')) 25 | ) 26 | 27 | repo.putIndex(testIndex0) 28 | repo.putPreviousHash(testPreviousHash0) 29 | 30 | expect( 31 | repo.getPreviousHash()?.getAll().getContentsCopy().toString('hex') 32 | ).toBe(testPreviousHash0?.getAll().getContentsCopy().toString('hex')) 33 | expect(repo.getIndex()?.getAll().getContentsCopy().toString('hex')).toBe( 34 | testIndex0?.getAll().getContentsCopy().toString('hex') 35 | ) 36 | 37 | await repo.commit() 38 | 39 | repo.putIndex(testIndex1) 40 | repo.putPreviousHash(testPreviousHash1) 41 | 42 | expect( 43 | repo.getPreviousHash()?.getAll().getContentsCopy().toString('hex') 44 | ).toBe(testPreviousHash1?.getAll().getContentsCopy().toString('hex')) 45 | expect(repo.getIndex()?.getAll().getContentsCopy().toString('hex')).toBe( 46 | testIndex1?.getAll().getContentsCopy().toString('hex') 47 | ) 48 | }) 49 | }) 50 | } 51 | 52 | const path = './test-state.json' 53 | const repository = new XyoFileOriginStateRepository(path) 54 | testOriginStateRepository(repository) 55 | -------------------------------------------------------------------------------- /src/persist/xyo-file-origin-state-repository.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | 3 | import { XyoBuffer, XyoStructure } from '../object-model' 4 | import XyoSigner from '../signing/xyo-signer' 5 | import XyoOriginStateRepository from './xyo-origin-state-repository' 6 | 7 | interface IXyoFileOriginState { 8 | index: string | undefined 9 | previousHash: string | undefined 10 | signers: string[] 11 | } 12 | 13 | export class XyoFileOriginStateRepository implements XyoOriginStateRepository { 14 | private indexCache: XyoStructure | undefined 15 | private previousHashCache: XyoStructure | undefined 16 | private signersCache: XyoSigner[] = [] 17 | private statePath: string 18 | 19 | constructor(statePath: string) { 20 | this.statePath = statePath 21 | } 22 | 23 | public addSigner(signer: XyoSigner) { 24 | this.signersCache.push(signer) 25 | } 26 | 27 | public removeOldestSigner() { 28 | if (this.signersCache.length > 0) { 29 | this.signersCache.shift() 30 | } 31 | } 32 | 33 | public putIndex(index: XyoStructure): void { 34 | this.indexCache = index 35 | } 36 | 37 | public putPreviousHash(previousHash: XyoStructure): void { 38 | this.previousHashCache = previousHash 39 | } 40 | 41 | public getIndex(): XyoStructure | undefined { 42 | return this.indexCache 43 | } 44 | 45 | public getPreviousHash(): XyoStructure | undefined { 46 | return this.previousHashCache 47 | } 48 | 49 | public getSigners(): XyoSigner[] { 50 | if (this.signersCache) { 51 | return this.signersCache 52 | } 53 | 54 | return [] 55 | } 56 | 57 | public commit(): Promise { 58 | const state = this.getCurrentFileState() 59 | 60 | return new Promise((resolve, reject) => { 61 | fs.writeFile(this.statePath, JSON.stringify(state), 'utf8', (error) => { 62 | if (error) { 63 | reject(error) 64 | } else { 65 | resolve() 66 | } 67 | }) 68 | }) 69 | } 70 | 71 | public async restore( 72 | signerFromPrivateKey: (buffer: Buffer) => XyoSigner 73 | ): Promise { 74 | const currentState = await this.readCurrentFileState() 75 | 76 | if (currentState) { 77 | if (currentState.index) { 78 | this.indexCache = new XyoStructure( 79 | new XyoBuffer(Buffer.from(currentState.index, 'base64')) 80 | ) 81 | } 82 | 83 | if (currentState.previousHash) { 84 | this.previousHashCache = new XyoStructure( 85 | new XyoBuffer(Buffer.from(currentState.previousHash, 'base64')) 86 | ) 87 | } 88 | 89 | currentState.signers.forEach((privateKey) => { 90 | this.signersCache.push( 91 | signerFromPrivateKey(Buffer.from(privateKey, 'base64')) 92 | ) 93 | }) 94 | } 95 | } 96 | 97 | private readCurrentFileState(): Promise { 98 | return new Promise((resolve, _reject) => { 99 | fs.readFile(this.statePath, (error, data) => { 100 | if (error) { 101 | resolve(undefined) 102 | return 103 | } 104 | 105 | const stringValue = data.toString('utf8') 106 | resolve(JSON.parse(stringValue) as IXyoFileOriginState) 107 | }) 108 | }) 109 | } 110 | 111 | private getCurrentFileState(): IXyoFileOriginState { 112 | const signersString: string[] = [] 113 | let previousHashString: string | undefined 114 | let indexString: string | undefined 115 | 116 | if (this.previousHashCache) { 117 | previousHashString = this.previousHashCache 118 | .getAll() 119 | .getContentsCopy() 120 | .toString('base64') 121 | } 122 | 123 | if (this.indexCache) { 124 | indexString = this.indexCache 125 | .getAll() 126 | .getContentsCopy() 127 | .toString('base64') 128 | } 129 | 130 | this.signersCache.forEach((signer) => { 131 | signersString.push( 132 | signer.getPrivateKey().getAll().getContentsCopy().toString('base64') 133 | ) 134 | }) 135 | 136 | const state: IXyoFileOriginState = { 137 | index: indexString, 138 | previousHash: previousHashString, 139 | signers: signersString, 140 | } 141 | 142 | return state 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/persist/xyo-memory-block-repository.ts: -------------------------------------------------------------------------------- 1 | import { XyoIterableStructure } from '../object-model' 2 | import { 3 | XyoOriginBlockGetter, 4 | XyoOriginBlockRepository, 5 | } from './xyo-origin-block-repository' 6 | 7 | export class XyoMemoryBlockRepository 8 | implements XyoOriginBlockRepository, XyoOriginBlockGetter { 9 | private blockMapping: Map = new Map() 10 | 11 | // eslint-disable-next-line require-await 12 | public async initialize(): Promise { 13 | return true 14 | } 15 | 16 | // eslint-disable-next-line require-await 17 | public async removeOriginBlock(hash: Buffer): Promise { 18 | this.blockMapping.delete(hash.toString('base64')) 19 | } 20 | 21 | // eslint-disable-next-line require-await 22 | public async addOriginBlock(hash: Buffer, block: Buffer): Promise { 23 | this.blockMapping.set(hash.toString('base64'), block) 24 | } 25 | 26 | // eslint-disable-next-line require-await 27 | public async getOriginBlock(hash: Buffer): Promise { 28 | return this.blockMapping.get(hash.toString('base64')) 29 | } 30 | 31 | // eslint-disable-next-line require-await 32 | public async addOriginBlocks(hashes: Buffer, blocks: Buffer): Promise { 33 | const blockStructure = new XyoIterableStructure(blocks) 34 | const hashesStructure = new XyoIterableStructure(hashes) 35 | const blockIt = blockStructure.newIterator() 36 | const hashIt = hashesStructure.newIterator() 37 | 38 | while (blockIt.hasNext()) { 39 | const block = blockIt.next().value 40 | const hash = hashIt.next().value 41 | this.addOriginBlock( 42 | hash.getAll().getContentsCopy(), 43 | block.getAll().getContentsCopy() 44 | ) 45 | } 46 | } 47 | 48 | // eslint-disable-next-line require-await 49 | public async getOriginBlocks( 50 | _limit?: number, 51 | _offset?: Buffer 52 | ): Promise<{ items: Buffer[]; total: number }> { 53 | throw new Error( 54 | 'getOriginBlocks not implemented in XyoMemoryBlockRepository' 55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-block-repository/BlockByPublicKeyRepository.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xyo-origin-block-repository copy 4.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 12:50:26 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 12:52:30 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | abstract class XyoBlockByPublicKeyRepository { 14 | abstract getOriginBlocksByPublicKey( 15 | publicKey: Buffer, 16 | index: number | undefined, 17 | limit: number | undefined, 18 | up: boolean 19 | ): Promise<{ items: Buffer[]; total: number }> 20 | } 21 | 22 | export default XyoBlockByPublicKeyRepository 23 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-block-repository/BlocksByGeohashRepository.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xyo-origin-block-repository copy.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 12:50:24 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 12:52:47 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | abstract class XyoBlocksByGeohashRepository { 14 | abstract getOriginBlocksByGeohash( 15 | geohash: string, 16 | limit: number 17 | ): Promise 18 | } 19 | 20 | export default XyoBlocksByGeohashRepository 21 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-block-repository/BlocksByTime.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xyo-origin-block-repository copy 3.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 12:50:25 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 12:53:30 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | abstract class XyoBlocksByTime { 14 | abstract getOriginBlocksByTime( 15 | fromTime: number, 16 | limit: number 17 | ): Promise<{ items: Buffer[]; lastTime: number }> 18 | } 19 | 20 | export default XyoBlocksByTime 21 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-block-repository/OriginBlockGetter.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xyo-origin-block-repository copy 3.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 12:50:25 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 12:53:40 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | abstract class XyoOriginBlockGetter { 14 | abstract initialize(): Promise 15 | 16 | abstract getOriginBlock(hash: Buffer): Promise 17 | abstract getOriginBlocks( 18 | limit?: number, 19 | offset?: Buffer 20 | ): Promise<{ items: Buffer[]; total: number }> 21 | } 22 | 23 | export default XyoOriginBlockGetter 24 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-block-repository/OriginBlockRepository.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: xyo-origin-block-repository copy 2.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 12:50:25 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 12:53:58 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | abstract class XyoOriginBlockRepository { 14 | abstract removeOriginBlock(hash: Buffer): Promise 15 | abstract addOriginBlock(hash: Buffer, block: Buffer): Promise 16 | abstract addOriginBlocks(hashes: Buffer, blocks: Buffer): Promise 17 | } 18 | 19 | export default XyoOriginBlockRepository 20 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-block-repository/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * File: index.ts 3 | * Project: @xyo-network/sdk-core-nodejs 4 | * File Created: Friday, 13th November 2020 12:54:05 pm 5 | * Author: XYO Development Team (support@xyo.network) 6 | * ----- 7 | * Last Modified: Friday, 13th November 2020 12:55:58 pm 8 | * Modified By: XYO Development Team (support@xyo.network>) 9 | * ----- 10 | * Copyright 2017 - 2020 XY - The Persistent Company 11 | */ 12 | 13 | import XyoBlockByPublicKeyRepository from './BlockByPublicKeyRepository' 14 | import XyoBlocksByGeohashRepository from './BlocksByGeohashRepository' 15 | import XyoBlocksByTime from './BlocksByTime' 16 | import XyoOriginBlockGetter from './OriginBlockGetter' 17 | import XyoOriginBlockRepository from './OriginBlockRepository' 18 | 19 | export { 20 | XyoBlockByPublicKeyRepository, 21 | XyoBlocksByGeohashRepository, 22 | XyoBlocksByTime, 23 | XyoOriginBlockGetter, 24 | XyoOriginBlockRepository, 25 | } 26 | -------------------------------------------------------------------------------- /src/persist/xyo-origin-state-repository.ts: -------------------------------------------------------------------------------- 1 | import { XyoStructure } from '../object-model' 2 | import XyoSigner from '../signing/xyo-signer' 3 | 4 | abstract class XyoOriginStateRepository { 5 | abstract putIndex(index: XyoStructure): void 6 | abstract putPreviousHash(previousHash: XyoStructure): void 7 | abstract addSigner(signer: XyoSigner): void 8 | 9 | abstract getIndex(): XyoStructure | undefined 10 | abstract getPreviousHash(): XyoStructure | undefined 11 | abstract getSigners(): XyoSigner[] 12 | abstract removeOldestSigner(): void 13 | 14 | abstract commit(): Promise 15 | } 16 | 17 | export default XyoOriginStateRepository 18 | -------------------------------------------------------------------------------- /src/schema/index.ts: -------------------------------------------------------------------------------- 1 | import { XyoSchema } from '../object-model' 2 | 3 | export class XyoObjectSchema { 4 | public static ARRAY_TYPED = new XyoSchema(1, 0xb0) 5 | public static ARRAY_UNTYPED = new XyoSchema(1, 0xb0) 6 | public static BW = new XyoSchema(2, 0xa0) 7 | public static INDEX = new XyoSchema(3, 0x80) 8 | public static NEXT_PUBLIC_KEY = new XyoSchema(4, 0x80) 9 | public static BRIDGE_BLOCK_SET = new XyoSchema(5, 0xa0) 10 | public static BRIDGE_HASH_SET = new XyoSchema(6, 0xb0) 11 | public static PAYMENT_KEY = new XyoSchema(7, 0x80) 12 | public static PREVIOUS_HASH = new XyoSchema(8, 0xa0) 13 | public static EC_SIGNATURE = new XyoSchema(9, 0x80) 14 | public static RSA_SIGNATURE = new XyoSchema(10, 0x80) 15 | public static STUB_SIGNATURE = new XyoSchema(11, 0x80) 16 | public static EC_PUBLIC_KEY = new XyoSchema(12, 0x80) 17 | 18 | public static RSA_PUBLIC_KEY = new XyoSchema(13, 0x80) 19 | public static STUB_PUBLIC_KEY = new XyoSchema(14, 0x80) 20 | public static STUB_HASH = new XyoSchema(15, 0x80) 21 | public static SHA_256 = new XyoSchema(16, 0x80) 22 | public static SHA_3 = new XyoSchema(17, 0x80) 23 | public static GPS = new XyoSchema(18, 0xa0) 24 | public static RSSI = new XyoSchema(19, 0x80) 25 | public static UNIX_TIME = new XyoSchema(20, 0x80) 26 | 27 | public static FETTER = new XyoSchema(21, 0xa0) 28 | public static FETTER_SET = new XyoSchema(22, 0xb0) 29 | public static WITNESS = new XyoSchema(23, 0xa0) 30 | public static WITNESS_SET = new XyoSchema(24, 0xb0) 31 | public static KEY_SET = new XyoSchema(25, 0xa0) 32 | public static SIGNATURE_SET = new XyoSchema(26, 0xa0) 33 | public static BW_FRAGMENT = new XyoSchema(27, 0xa0) 34 | public static LAT = new XyoSchema(28, 0x80) 35 | public static LNG = new XyoSchema(29, 0x80) 36 | public static BLE_POWER_LEVEL = new XyoSchema(30, 0x80) 37 | 38 | public static EC_PRIVATE_KEY = new XyoSchema(0xff, 0x80) 39 | } 40 | -------------------------------------------------------------------------------- /src/signing/ecdsa/index.ts: -------------------------------------------------------------------------------- 1 | export { XyoSecp2556k1 } from './xyo-secp256k1' 2 | -------------------------------------------------------------------------------- /src/signing/ecdsa/xyo-secp256k1.ts: -------------------------------------------------------------------------------- 1 | import elliptic from 'elliptic' 2 | 3 | import { XyoBuffer, XyoStructure } from '../../object-model' 4 | import { XyoObjectSchema } from '../../schema' 5 | import XyoSigner, { XyoSignatureVerify } from '../xyo-signer' 6 | 7 | const ec = new elliptic.ec('secp256k1') 8 | 9 | export class XyoSecp2556k1 implements XyoSigner { 10 | public static verify: XyoSignatureVerify = async ( 11 | publicKey: Buffer, 12 | signature: Buffer, 13 | data: Buffer 14 | // eslint-disable-next-line require-await 15 | ): Promise => { 16 | const signatureStructure = new XyoStructure(new XyoBuffer(signature)) 17 | const publicKeyStructure = new XyoStructure(new XyoBuffer(publicKey)) 18 | const derSignature = XyoSecp2556k1.buildDerSignature( 19 | signatureStructure.getValue().getContentsCopy() 20 | ) 21 | 22 | const x = publicKeyStructure.getValue().copyRangeOf(0, 31) 23 | const y = publicKeyStructure.getValue().copyRangeOf(32, 64) 24 | const hexKey = ['04', x, y].join('') 25 | const key = ec.keyFromPublic(hexKey, 'hex') 26 | return key.verify(data, derSignature) 27 | } 28 | 29 | private static buildDerSignature(xyBuffer: Buffer) { 30 | const sizeOfR = xyBuffer.readUInt8(0) 31 | const rBuffer = xyBuffer.slice(1, sizeOfR + 1) 32 | 33 | const source = Buffer.concat([ 34 | Buffer.from([0x02]), 35 | xyBuffer.slice(0, 1), 36 | rBuffer, 37 | Buffer.from([0x02]), 38 | xyBuffer.slice(sizeOfR + 1), 39 | ]) 40 | 41 | const sourceBufferSizeBuffer = Buffer.alloc(1) 42 | sourceBufferSizeBuffer.writeUInt8(source.length, 0) 43 | 44 | return Buffer.concat([ 45 | Buffer.from([0x30]), 46 | sourceBufferSizeBuffer, 47 | source, 48 | ]).toString('hex') 49 | } 50 | 51 | private key: elliptic.ec.KeyPair 52 | 53 | constructor(key?: Buffer) { 54 | if (key) { 55 | const structure = new XyoStructure(key) 56 | const privateKey = structure.getValue().getContentsCopy() 57 | this.key = ec.keyFromPrivate(privateKey) 58 | return 59 | } 60 | 61 | this.key = ec.genKeyPair() 62 | } 63 | 64 | public sign(data: Buffer): XyoStructure { 65 | const signature = this.key.sign(data) 66 | 67 | const rBuffer = signature.r.toBuffer() 68 | const sBuffer = signature.s.toBuffer() 69 | 70 | const value = Buffer.concat([ 71 | Buffer.from([rBuffer.length]), 72 | rBuffer, 73 | Buffer.from([sBuffer.length]), 74 | sBuffer, 75 | ]) 76 | 77 | return XyoStructure.newInstance( 78 | XyoObjectSchema.EC_SIGNATURE, 79 | new XyoBuffer(value) 80 | ) 81 | } 82 | 83 | public getPublicKey(): XyoStructure { 84 | const key = this.key.getPublic() 85 | const x = key.getX().toBuffer() 86 | const y = key.getY().toBuffer() 87 | 88 | const buffer = Buffer.concat([ 89 | this.writePointTo32ByteBuffer(x), 90 | this.writePointTo32ByteBuffer(y), 91 | ]) 92 | 93 | return XyoStructure.newInstance( 94 | XyoObjectSchema.EC_PUBLIC_KEY, 95 | new XyoBuffer(buffer) 96 | ) 97 | } 98 | 99 | public getPrivateKey(): XyoStructure { 100 | const privateKey = this.key.getPrivate('hex').toString() 101 | const buffer = new XyoBuffer(Buffer.from(privateKey, 'hex')) 102 | 103 | return XyoStructure.newInstance(XyoObjectSchema.EC_PRIVATE_KEY, buffer) 104 | } 105 | 106 | private writePointTo32ByteBuffer(point: Buffer) { 107 | if (point.length === 32) { 108 | return point 109 | } 110 | const dest = Buffer.alloc(32) 111 | const offset = dest.length - point.length 112 | let index = 0 113 | 114 | while (index < point.length) { 115 | dest[offset + index] = point[index] 116 | index += 1 117 | } 118 | 119 | return dest 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/signing/index.ts: -------------------------------------------------------------------------------- 1 | import XyoSigner from './xyo-signer' 2 | export * from './ecdsa' 3 | export { XyoSigner } 4 | -------------------------------------------------------------------------------- /src/signing/xyo-signer.ts: -------------------------------------------------------------------------------- 1 | import { XyoStructure } from '../object-model' 2 | 3 | abstract class XyoSigner { 4 | abstract sign(data: Buffer): XyoStructure 5 | abstract getPublicKey(): XyoStructure 6 | abstract getPrivateKey(): XyoStructure 7 | } 8 | 9 | export type XyoSignatureVerify = ( 10 | publicKey: Buffer, 11 | signature: Buffer, 12 | data: Buffer 13 | ) => Promise 14 | 15 | export default XyoSigner 16 | -------------------------------------------------------------------------------- /src/simulation/index.ts: -------------------------------------------------------------------------------- 1 | import { XyoZigZagBoundWitness } from '../bound-witness/xyo-zig-zag-bound-witness' 2 | import { XyoBuffer, XyoStructure } from '../object-model' 3 | import { XyoOriginState } from '../origin/xyo-origin-state' 4 | import { XyoObjectSchema } from '../schema' 5 | import { XyoRamOriginStateRepository } from './xyo-ram-origin-state-repository' 6 | import { XyoStubSigner } from './xyo-stub-signature' 7 | 8 | interface IXyoJsonBoundWitnessCreator { 9 | createBlocksFromJson(json: string): XyoZigZagBoundWitness[] | Error 10 | } 11 | 12 | export class XyoJsonBoundWitnessCreator implements IXyoJsonBoundWitnessCreator { 13 | private partyToState: Map = new Map() 14 | 15 | // tslint:disable-next-line:prefer-array-literal 16 | public createBlockfromJson(boundWitness: { 17 | [key: string]: any 18 | }): XyoZigZagBoundWitness | undefined { 19 | const allKeys = Object.keys(boundWitness) 20 | if (allKeys.length > 2 || allKeys.length === 0) { 21 | throw new Error('Can only support two parties per BoundWitness') 22 | } 23 | if (allKeys.length === 2) { 24 | const alice = allKeys[0] as string 25 | const bob = allKeys[1] as string 26 | const aliceState = this.getPartyState(alice) 27 | const bobState = this.getPartyState(bob) 28 | 29 | const aliceHeuristics = this.createCustomHeuristicArrayWithDefaults( 30 | [], 31 | alice 32 | ) 33 | const bobHeuristics = this.createCustomHeuristicArrayWithDefaults([], bob) 34 | 35 | const boundWitnessAlice = new XyoZigZagBoundWitness( 36 | aliceState.getSigners(), 37 | aliceHeuristics, 38 | [] 39 | ) 40 | const boundWitnessBob = new XyoZigZagBoundWitness( 41 | bobState.getSigners(), 42 | bobHeuristics, 43 | [] 44 | ) 45 | 46 | const aliceToBobOne = boundWitnessAlice.incomingData(undefined, false) 47 | const bobToAliceOne = boundWitnessBob.incomingData(aliceToBobOne, true) 48 | const aliceToBobTwo = boundWitnessAlice.incomingData(bobToAliceOne, false) 49 | boundWitnessBob.incomingData(aliceToBobTwo, false) 50 | 51 | aliceState.addOriginBlock(boundWitnessAlice) 52 | bobState.addOriginBlock(boundWitnessBob) 53 | return boundWitnessAlice 54 | } 55 | 56 | return this.createSelfSignedBoundWitness(allKeys[0]) 57 | } 58 | 59 | public createBlocksFromJson(json: string): XyoZigZagBoundWitness[] { 60 | const bwArray: XyoZigZagBoundWitness[] = [] 61 | const userData = JSON.parse(json) as Array<{ [key: string]: any }> 62 | 63 | userData.forEach((boundWitness) => { 64 | const cBw = this.createBlockfromJson(boundWitness) 65 | 66 | if (cBw) { 67 | bwArray.push(cBw) 68 | } 69 | }) 70 | this.partyToState = new Map() 71 | return bwArray 72 | } 73 | 74 | private createSelfSignedBoundWitness(party: string): XyoZigZagBoundWitness { 75 | const charlieHeuristics = this.createCustomHeuristicArrayWithDefaults( 76 | [], 77 | party 78 | ) 79 | const charlieState = this.getPartyState(party) 80 | const boundWitnessCharlie = new XyoZigZagBoundWitness( 81 | charlieState.getSigners(), 82 | charlieHeuristics, 83 | [] 84 | ) 85 | boundWitnessCharlie.incomingData(undefined, true) 86 | return boundWitnessCharlie 87 | } 88 | 89 | private createCustomHeuristicArrayWithDefaults( 90 | heuristics: { [key: string]: any }, 91 | party: string 92 | ): XyoStructure[] { 93 | const desiredHeuristics = Object.keys(heuristics) 94 | const returnedHeuristics: XyoStructure[] = [] 95 | desiredHeuristics.forEach((heuristic) => { 96 | const newHeuristic = this.createHeuristic( 97 | heuristic, 98 | heuristics[heuristic] 99 | ) 100 | 101 | if (newHeuristic) { 102 | returnedHeuristics.push(newHeuristic) 103 | } 104 | }) 105 | return this.appendNeededHeuristics(returnedHeuristics, party) 106 | } 107 | 108 | private createHeuristic( 109 | heuristic: string, 110 | value: number | string 111 | ): XyoStructure | undefined { 112 | switch (heuristic) { 113 | case 'rssi': { 114 | const numBuff = Buffer.alloc(1) 115 | numBuff.writeInt8(value as number, 0) 116 | const buffer = new XyoBuffer(numBuff) 117 | return XyoStructure.newInstance(XyoObjectSchema.RSSI, buffer) 118 | } 119 | case 'time': { 120 | const numBuff = Buffer.alloc(8) 121 | numBuff.writeUIntBE(value as number, 2, 6) 122 | const buffer = new XyoBuffer(numBuff) 123 | return XyoStructure.newInstance(XyoObjectSchema.UNIX_TIME, buffer) 124 | } 125 | } 126 | } 127 | 128 | private appendNeededHeuristics( 129 | heuristcs: any[], 130 | party: string 131 | ): XyoStructure[] { 132 | const state = this.getPartyState(party) 133 | if (!state) { 134 | return [] 135 | } 136 | 137 | const hash = state.getPreviousHash() 138 | if (hash) { 139 | heuristcs.push(hash) 140 | } 141 | 142 | heuristcs.push(state.getIndex()) 143 | return heuristcs 144 | } 145 | 146 | private getPartyState(party: string): XyoOriginState { 147 | let partyState = this.partyToState.get(party) 148 | if (!partyState) { 149 | const signer = new XyoStubSigner(Buffer.from(party)) 150 | const stateRepo = new XyoRamOriginStateRepository() 151 | const state = new XyoOriginState(stateRepo) 152 | state.addSigner(signer) 153 | this.partyToState.set(party, state) 154 | partyState = this.partyToState.get(party) 155 | } 156 | return partyState as XyoOriginState 157 | } 158 | 159 | private setPartyState(party: string, state: XyoOriginState): void { 160 | this.partyToState.set(party, state) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/simulation/spec/xyo-bound-witness-json.spec.ts: -------------------------------------------------------------------------------- 1 | import { XyoJsonBoundWitnessCreator } from '../index' 2 | describe('Json to BoundWitness', () => { 3 | it('2 Party Block', () => { 4 | const boundWitnessBytes = 5 | '[\ 6 | {\ 7 | "partyA":{\ 8 | "time": 2\ 9 | },\ 10 | "partyB":{\ 11 | "rssi": 3\ 12 | }\ 13 | },\ 14 | {\ 15 | "partyA":{\ 16 | "time": 30\ 17 | },\ 18 | "partyC":{\ 19 | "time": 50\ 20 | }\ 21 | }\ 22 | ]' 23 | 24 | const jsonCreator = new XyoJsonBoundWitnessCreator() 25 | const createdBoundWitness = jsonCreator.createBlocksFromJson( 26 | boundWitnessBytes 27 | ) 28 | expect(createdBoundWitness[0].getIsCompleted()) 29 | expect(createdBoundWitness[1].getIsCompleted()) 30 | }) 31 | 32 | it('1 Party Block', () => { 33 | const boundWitnessBytes = 34 | '[\ 35 | {\ 36 | "partyD":{\ 37 | "rssi": 30\ 38 | }}\ 39 | ]' 40 | const jsonCreator = new XyoJsonBoundWitnessCreator() 41 | const createdBoundWitness = jsonCreator.createBlocksFromJson( 42 | boundWitnessBytes 43 | ) 44 | expect(createdBoundWitness[0].getIsCompleted()) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /src/simulation/xyo-ram-origin-state-repository.ts: -------------------------------------------------------------------------------- 1 | import { XyoStructure } from '../object-model' 2 | import XyoOriginStateRepository from '../persist/xyo-origin-state-repository' 3 | import XyoSigner from '../signing/xyo-signer' 4 | 5 | export class XyoRamOriginStateRepository implements XyoOriginStateRepository { 6 | private indexCache: XyoStructure | undefined 7 | private previousHashCache: XyoStructure | undefined 8 | private signersCache: XyoSigner[] = [] 9 | 10 | public addSigner(signer: XyoSigner) { 11 | this.signersCache.push(signer) 12 | } 13 | 14 | public removeOldestSigner() { 15 | if (this.signersCache.length > 0) { 16 | this.signersCache.shift() 17 | } 18 | } 19 | 20 | public putIndex(index: XyoStructure): void { 21 | this.indexCache = index 22 | } 23 | 24 | public putPreviousHash(previousHash: XyoStructure): void { 25 | this.previousHashCache = previousHash 26 | } 27 | 28 | public getIndex(): XyoStructure | undefined { 29 | return this.indexCache 30 | } 31 | 32 | public getPreviousHash(): XyoStructure | undefined { 33 | return this.previousHashCache 34 | } 35 | 36 | public getSigners(): XyoSigner[] { 37 | if (this.signersCache) { 38 | return this.signersCache 39 | } 40 | 41 | return [] 42 | } 43 | 44 | // eslint-disable-next-line require-await 45 | public async commit() { 46 | return 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/simulation/xyo-stub-signature.ts: -------------------------------------------------------------------------------- 1 | import { XyoBuffer, XyoStructure } from '../object-model' 2 | import { XyoObjectSchema } from '../schema' 3 | import XyoSigner, { XyoSignatureVerify } from '../signing/xyo-signer' 4 | 5 | export class XyoStubSigner implements XyoSigner { 6 | public static verify: XyoSignatureVerify = async ( 7 | _publicKey: Buffer, 8 | _signature: Buffer, 9 | _data: Buffer 10 | // eslint-disable-next-line require-await 11 | ): Promise => { 12 | return true 13 | } 14 | private key: Buffer 15 | 16 | constructor(key?: Buffer) { 17 | if (key) { 18 | this.key = key 19 | return 20 | } 21 | throw new Error('Stub signer a needs key!') 22 | } 23 | 24 | public sign(data: Buffer): XyoStructure { 25 | return XyoStructure.newInstance( 26 | XyoObjectSchema.STUB_SIGNATURE, 27 | new XyoBuffer(data) 28 | ) 29 | } 30 | 31 | public getPublicKey(): XyoStructure { 32 | return XyoStructure.newInstance( 33 | XyoObjectSchema.STUB_PUBLIC_KEY, 34 | new XyoBuffer(this.key) 35 | ) 36 | } 37 | 38 | public getPrivateKey(): XyoStructure { 39 | return XyoStructure.newInstance( 40 | XyoObjectSchema.EC_PRIVATE_KEY, 41 | new XyoBuffer(Buffer.from('0000', 'utf8')) 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/types/bs58.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'bs58' { 2 | export function encode(buffer: Buffer): string 3 | export function decodeUnsafe(string: string): Buffer | undefined 4 | export function decode(string: string): Buffer 5 | } 6 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tsconfig", 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "dist" 6 | }, 7 | "include": ["src"], 8 | "exclude": ["*.spec.ts", "**/spec/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "downlevelIteration": true, 6 | "target": "es6", 7 | "skipLibCheck": true, 8 | "lib": ["esnext"], 9 | "esModuleInterop": true, 10 | "alwaysStrict": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "module": "CommonJS", 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "sourceMap": true, 19 | "composite": true, 20 | "declarationMap": true, 21 | "declaration": true, 22 | "outDir": "out/spec", 23 | "baseUrl": "." 24 | }, 25 | "include": ["src"] 26 | } 27 | --------------------------------------------------------------------------------